递归反转链表(斯坦福CS库中的代码)解释

时间:2012-09-01 19:18:40

标签: recursion linked-list reverse

我的递归技巧非常生疏。我一直在考虑这个问题,并在论坛上搜索了很长时间但仍然无法理解。现在我正在寻找递归反转来自Stanford CS ed库的链表代码。

#include <stdio.h> 

struct Node {
  int x;
  struct Node *next;
};

void Reverse(struct Node ** headRef){
    struct Node* first;
    struct Node* rest;

    if(*headRef==NULL)
       return;

    first= *headRef;
    rest= first->next;  

    if(rest==NULL)
        return; 

    Reverse(&rest);
    printf("Rest%d\n", (rest)->x); // I added this line to print rest
    first->next->next=first;
    first->next=NULL;
    *headRef=rest;
}

void printList(struct Node* head){

    if(!head)
       return;
    else{
       printf("%d ", head->x);
       printList(head->next);   
    }
}

void main(){
    struct Node *head;
    struct Node * node1= (struct Node*) malloc(sizeof(struct Node));
    struct Node * node2= (struct Node*) malloc(sizeof(struct Node));
    struct Node * node3= (struct Node*) malloc(sizeof(struct Node));
    struct Node * node4= (struct Node*) malloc(sizeof(struct Node));
    head= node1;
    node1->next=node2;
    node1->x=1;
    node2->x=2;
    node3->x=3;
    node4->x=4;
    node2->next=node3;
    node3->next=node4;
    node4->next=NULL;
    Reverse(&head);
}

现在假设我有一个链表1-&gt; 2-&gt; 3-&gt; 4。我无法理解的是最后一行,它最终将headRef设置为4,我认为它应该将headRef设置为2.我试图执行该函数并将其打印出来:

  

4
  4
  4

表示变量rest。

但是,如果我在反转功能中注释了最后一行,它仍然会反转列表但会打印

  

4
  3
  2。

我能理解的第二个结果,但第一个结果似乎很混乱。声明“* headRef = rest”是否对变量rest做了什么?它一直指向4?

另外,如果我传递* headRef而不是** headRef(最后一行没有注释掉),它会打印结果

  

4
  3
  2

太。

有人可以向我解释记忆中发生了什么吗?太感谢了。

2 个答案:

答案 0 :(得分:2)

在递归致电Reverse之前,我们有:

first---|  
        |  
        v  
        1->2->3->4->NULL
           ^
           |
           |
  rest------

在递归调用Reverse后,我们有:

first---|
        |
        v
        1->2<-3<-4
           |     ^
           v     |
         NULL    |
  rest------------

现在我们需要2->NULL2->1修改为first->next->next=first

first---|
        |
        v
        1<-2<-3<-4
        |  ^     ^
        |--|     |
                 |
  rest------------

现在我们需要1->21->NULL修改为first->next=NULL

first---|
        |
        v
  NULL<-1<-2<-3<-4
                 ^
                 |
                 |
  rest------------

最后*headRef=rest,以便*headRef指向4而不是1

答案 1 :(得分:0)

这里发生的是因为递归调用将 rest的地址传递给局部变量headRef ,当每次递归调用返回时,语句* headRef = rest已经更改执行流程中接下来的语句的rest指针的地址。

对于链接列表1-&gt; 2-&gt; 3-&gt; 4:

我们假设1存储在地址100中,2表示存储在地址200中,3表示存储在地址300中,4表示存储在地址400中。

第1部分: 调用Reverse(&amp; rest)[rest points to address 400]

首先= 400 rest = NULL

当rest为NULL时,执行返回到Reverse(400)调用之后的点

第2部分: 这里首先= 300,休息= 400 在执行first-&gt; next-&gt; next = first和first-&gt; next = NULL

之后

我们有* headRef =休息[休息点为400]

但是这个headRef被传递了一个rest = 300的地址。所以现在已经为下一步执行, 休息点指向400。

第3部分: 现在执行返回到Reverse(300)调用

之后的点

但是在前锋召唤期间[首先是200而休息是300]并且在回归期间[休息= 400]。 这是罢工!!!

执行first-&gt; next-&gt; next = first和first-&gt; next = NULL

我们有* headRef =休息[休息点为400]

但是这个headRef被传递了一个rest = 200的地址。所以现在已经为下一步执行, 休息点指向400.

第4部分: 现在执行返回到Reverse(200)调用

之后的点

但是在前锋召唤期间[首先是100而休息是200]并且在回归期间[休息= 400]。

执行first-&gt; next-&gt; next = first和first-&gt; next = NULL

我们有* headRef =休息[休息点为400]

因为这是初始调用,函数返回* headRef,其值为400。

工作完成!