在c中递归地反转链表

Posted

技术标签:

【中文标题】在c中递归地反转链表【英文标题】:Reversing a linkedlist recursively in c 【发布时间】:2012-12-14 09:11:14 【问题描述】:

当 head 作为参数发送给它时,以下代码可以正常工作。由于我是 C 新手,所以我无法理解它是如何工作的。请帮帮我。

struct node *recursiveReverseLL(struct node *list)

    struct node *revHead;
    if (list == NULL || list->link == NULL)
    
        return list;
    

    revHead = recursiveReverseLL(list->link);
    list->link->link = list;
    list->link = NULL; 

    return revHead;

我不知道如何使用这些递归调用提供链接。即)如果链接是,

1 -> 2 -> 3 -> 4 

然后hw改成,

4 -> 3 -> 2 -> 1

【问题讨论】:

请更准确地定义你不清楚的地方 我不知道这些递归调用是如何提供链接的 用通用和最基本的术语思考解决方案。最小的将是 2 个节点 1->2->null 的列表。为了使其通用,我们将始终从 first 节点引用其他节点。要扭转这种情况,请将first(1)->next(2)->next(null) = first(1) 设置为1<->2,然后first(1)->next(2) = null 将导致null<-1<-2。递归使用此规则。 【参考方案1】:

对此的一般递归算法是:

    Divide2 部分中的列表 - 第一 节点和列表的其余部分。 递归调用反向的rest 链表。 将rest链接到first。 修复head指针

这是带有内联 cmets 的代码:

struct node* recursiveReverseLL(struct node* first)

   if(first == NULL) return NULL; // list does not exist.

   if(first->link == NULL) return first; // list with only one node.

   struct node* rest = recursiveReverseLL(first->link); // recursive call on rest.

   first->link->link = first; // make first; link to the last node in the reversed rest.

   first->link = NULL; // since first is the new last, make its link NULL.

   return rest; // rest now points to the head of the reversed list.

我希望这张照片能让事情更清楚:

(来源:geeksforgeeks.org) .

【讨论】:

我花了一些时间才理解它。但无论如何,这是一个很好的解决方案。 基本上你会转到最后一个节点并不断返回指向它的指针,同时切换节点之间的链接。我做对了吗? 这是不必要的递归,而是can be fully iterative - 更高效也更清晰。【参考方案2】:

另一种解决方案:

struct node *reverse_recur(struct node *temp)

    if(temp->link==NULL)
    
        return temp;
    

    struct node *temp1=temp->link;

    temp->link=NULL;

    return (reverse_recur(temp1)->link=temp);


【讨论】:

【参考方案3】:

替代解决方案:

struct node *head;
void reverse(struct node *prev, struct node *cur)

   if(cur)
      reverse(cur,cur->link);
      cur->link = prev;
    
    else
      head = prev;
    

在main中,调用reverse(NULL,head);

【讨论】:

一种更优雅的调用方式可能是将它包装在另一个函数中,这只会占用头部。【参考方案4】:

设链表为 1-> 2 -> 3 ->4

c中的函数是--

struct linked_node * reverse_recursive(struct linked_node * head)

struct linked_node * first;/*stores the address of first node of the linked
list passed to function*/
struct linked_node * second;/* stores the address of second node of the
linked list passed to function*/
struct linked_node * rev_head;/*stores the address of last node of initial 
linked list. It also becomes the head of the reversed linked list.*/
//initalizing first and second
first=head;
second=head->next;
//if the linked list is empty then returns null
if(first=NULL)
   return(NULL);
/* if the linked list passed to function contains just 1 element, then pass
address of that element*/
if(second==NULL)
   return(first);
/*In the linked list passed to function, make the next of first element 
 NULL. It will eventually (after all the recursive calls ) make the
 next of first element of the initial linked list NULL.*/
first->next=NULL;
/* storing the address of the reverse head which will be passed to it by the
 condition if(second==NULL) hence it will store the address of last element
 when this statement is executed for the last time. Also here we assume that 
the reverse function will yield the reverse of the rest of the linked 
list.*/
rev_head=reverse(second);
/*making the rest of the linked list point to the first element. i.e. 
 reversing the list.*/
second->next=first;

/*returning the reverse head (address of last element of initial linked 
list) . This condition executes only if the initial list is 1- not empty 
2- contains more than one element. So it just relays the value of last 
element to higher recursive calls.  */
return(rev_head);

现在运行链表 1-> 2-> 3 -> 4 的函数

内反(&1) 代码一直运行到 rev_head=reverse(&2); // 这里&1是1的地址。

函数列表是 1(第一)->2(第二)-> 3-> 4

内反向(&2) 代码运行直到 rev_head=reverse(&3); 功能列表 2(第一)->3(第二)-> 4

内反向(&3) 代码运行直到 rev_head=reverse (&4); 列出 if 函数 3(第一)-> 4(第二)

内反向(&4) 终止条件 second==NULL 为真,因此执行 return 并 返回地址 4。

函数列表

4(第一)-> NULL(第二)

回退(&3) 函数列表是 NULL 以及返回的 rev_head=&4 的值

执行第二次后->next=first; 列表变成了

NULL

返回 (rev_head);被执行通过 &4 因为 rev_head=&4

返回 rev(&2)

函数中的列表是

NULL

而rev_head是&4,它是由rev(&3)返回的

执行 second->next=first 后,列表变为

NULL

return(rev_head);执行返回 &4 到 rev(&1);

返回 rev(&1)

函数中的列表是

NULL

rev_head 的值为 &4,由 reverse(&3) 传递

现在 second->next =first 被执行,列表变为

NULL

return(rev_head);被执行 // rev_head=&4 由 reverse(&2) 返回 并将 rev_head 的值传递给 main 函数。

希望这会有所帮助。我花了很多时间来理解这一点并写下这个答案。

【讨论】:

请在使用前检查函数命名法。 reverse 未声明。【参考方案5】:
    To fix head also:

void reverse_list_recursive_internal (struct list **head, struct list *node)

    /* last node, fix the head */
    if (node->next == NULL) 
        *head = node;
        return; 
    
    reverse_list_recursive_internal(head, node->next);
    node->next->next = node;
    node->next = NULL;


void reverse_list_recursive (struct list **head)

    if (*head == NULL) 
        return;
    
    reverse_list_recursive_internal(head, *head);

【讨论】:

【参考方案6】:

这是一种可以递归反转 SLL 的绝妙方法:

1.    struct node* head; // global head
2.    void rec_reverse(struct node* prev, struct node* cur)
3.    
4.        if (cur)
5.        
6.            rec_reverse(cur, cur->next);
7.            cur->next = prev;
8.        
9.        else
10.            head = prev;
11.    

这样调用函数:

rec_reverse(NULL, head);

方法:

递归调用函数(第 6 行)我们转到最后一个节点 链表。 然后我们用最后一个节点的地址更新head (第 10 行)。 最后,我们将每个节点的链接指向其前一个节点(第 7 行)。

【讨论】:

“美丽”值得商榷。通过仅添加一个临时变量来保存 curr->next 值,您可以交换两行代码,使递归调用位于 tail 位置,这一点更为重要。并且代码变得更加清晰易懂:void rec_reverse(struct node* prev, struct node* cur) if (cur==NULL) head = prev; else next = cur->next; cur->next = prev; rec_reverse(cur, next); .【参考方案7】:
/* Reverses a linked list, returns head of reversed list
*/
NodePtr reverseList(NodePtr curr) 
    if (curr == NULL || curr->next == NULL) return curr; // empty or single element case

    NodePtr nextElement = curr->next;
    curr->next = NULL;
    NodePtr head = reverseList(nextElement);
    nextElement->next = curr;
    return head;

【讨论】:

此方法每次迭代使用一个额外的堆栈空间(NextElement)。【参考方案8】:

在我看来,没有人提出尾递归的算法。原则上,尾递归算法可以在没有堆栈的情况下编译(前提是编译器足够聪明),从而生成消耗更少内存的代码。

假设TList是单链表的自定义数据类型,它是一个指向结构的指针,作为link字段用于访问列表中的下一个元素。

算法如下:

```

TList reverse_aux(TList l, TList solution) 
    if (l == NULL) 
        return solution;
     else 
        TList tmp = l->link;
        l->link = solution;
        return reverse_aux(tmp, l);
    


TList reverse(TList l) 
    return reverse_aux(l, NULL);

```

【讨论】:

【参考方案9】:
ll *rev_list(ll *prev, ll *cur)

    if (!cur) 
        return prev;
    

    ll *tmp = cur;
    cur = cur->next;
    tmp->next = prev;
    prev = tmp;
    return rev_list(prev, cur);

查找完整代码:https://github.com/vijaythreadtemp/Data-Structures-And-Algorithms/blob/master/rev_link_list_rec.cxx

【讨论】:

以上是关于在c中递归地反转链表的主要内容,如果未能解决你的问题,请参考以下文章

求c程序反转链表

Leetcode练习(Python):链表类:第206题:反转链表:反转一个单链表。

LeetCode206. 反转链表

使用递归反转链表

反转链表(递归链表)爬楼梯(记忆化搜索数学)旋转数组(数组数学)

单向链表反转,就地逆置与递归反转(无表头结点)