繁体   English   中英

递归链表反向算法中base case返回值的解释

[英]Explanation for base case return value in recursive linked list reverse algorithm

我正在学习如何递归地反转链表。 我对最后 4 行感到困惑。

 node *reverse_linked_list_rec(node *head){

  if (head->next==NULL){
    return head;
  }
  
  node *smallans= reverse_linked_list_rec(head->next);
 
  node *tail = head->next;
  tail->next = head;
  head->next = NULL;
   
  return smallans;
    
}

假设我正在倒车

1 2 3 NULL

通过递归,它达到3 NULL然后通过基本情况返回

2 3 NULL

这里head=2smallans=2 (不确定)。

为什么我们在这里返回smallAns以及它是如何变化的?

smallans是一个令人困惑的变量名,因为它实际上是通过列表传回的旧尾部成为最终返回给调用者的新头部。

当这些行在父函数调用中执行时,它的next指针会发生变化:

// when head->next->next == NULL ...
node *tail = head->next; // ... `tail` points to the old tail (new head) ...

tail->next = head; // ... and this sets the new tail's next pointer to
                   // the old second-to-last node (new second node).

tail在这里是一个误导性的名称——我将“尾”与终止整个列表的单个节点相关联,而不是前一个节点。 new_prevold_next在这里似乎更合适,具体取决于您是要命名与新列表还是原始列表中的节点角色相关的事物。

作为一个小问题,我建议使用if (!head || !head->next)来避免潜在的空指针取消引用。

我会按如下方式编写函数:

node *reverse_linked_list_rec(node *head) {
  if (!head || !head->next) {
    return head;
  }
  
  node *old_tail = reverse_linked_list_rec(head->next); 
  node *old_next = head->next;
  old_next->next = head;
  head->next = NULL;
  return old_tail;
}

除了求知欲之外,递归对于链表操作来说是一个糟糕的选择,因为它增加了函数调用开销,你可以炸毁堆栈,并且在大多数情况下,逻辑并不比迭代更容易遵循。

举个例子,这是一个带有迭代版本的完整示例:

#include <stdio.h>
#include <stdlib.h>

struct node {
    int id;
    struct node *next;
};

struct node *make_node(int id) {
    struct node *n = malloc(sizeof(*n));

    if (!n) exit(1);

    n->id = id;
    n->next = NULL;
    return n;
}

struct node *reverse_linked_list(struct node *head) {
    struct node *prev = NULL;
    
    for (struct node *curr = head; curr;) {
        struct node *old_next = curr->next;
        curr->next = prev;
        prev = curr;
        curr = old_next;
    }

    return prev;
}

void print_linked_list(struct node *head) {
    for (; head; head = head->next) {
        printf("%d->", head->id);
    }

    puts("");
}

void free_linked_list(struct node *head) {
    while (head) {
        struct node *tmp = head;
        head = head->next;
        free(tmp);
    }
}

int main() {
    struct node *head = make_node(1);
    head->next = make_node(2);
    head->next->next = make_node(3);
    print_linked_list(head); // => 1->2->3->
    head = reverse_linked_list(head);
    print_linked_list(head); // => 3->2->1->
    free_linked_list(head);
    return 0;
}

作为另一个小问题,由于链表正在发生变异,我可能会选择像void reverse_linked_list(struct node **head);这样的标题void reverse_linked_list(struct node **head); . 否则,调用非 void 函数似乎太容易了,忽略返回值并在调用者作用域中的head (已成为指向 null 的尾部)被取消引用时导致内存泄漏或崩溃。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM