C 将头引用标记为链接列表的尾部
反转链表的迭代方法非常简单。我通过下面的链接试图理解递归方法 我们首先将指针移动到链表的尾部。 在堆栈展开期间,我们反向缝合链接。C 将头引用标记为链接列表的尾部,c,linked-list,C,Linked List,反转链表的迭代方法非常简单。我通过下面的链接试图理解递归方法 我们首先将指针移动到链表的尾部。 在堆栈展开期间,我们反向缝合链接。 但对于所有堆栈展开调用,*headref=rest。既然第一个在堆栈展开期间更改为上一个堆栈值,为什么其余的也没有更改呢。我创建了4个节点,并通过gdb查看了这些值。堆栈展开期间的剩余值保持不变,但第一个值正在更改。为什么它没有改变 从改变指针和值的角度思考是一个好方法 考虑迭代程序,这是一种令人困惑的方法 递归程序,因为每个递归调用都创建自己的本地 变量,所有变
但对于所有堆栈展开调用,*headref=rest。既然第一个在堆栈展开期间更改为上一个堆栈值,为什么其余的也没有更改呢。我创建了4个节点,并通过gdb查看了这些值。堆栈展开期间的剩余值保持不变,但第一个值正在更改。为什么它没有改变 从改变指针和值的角度思考是一个好方法 考虑迭代程序,这是一种令人困惑的方法 递归程序,因为每个递归调用都创建自己的本地 变量,所有变量的名称相同,但值可能不同 对于递归函数,假设它对大小为
n
的输入正确工作,然后对大小为n+1
的输入验证其正确性更为有用。如果覆盖了“基本情况”(大小0或1),则证明它适用于所有输入
在您的例子中,我们假设recursiverse
对于长度为3的列表可以正常工作,并通过以下方式为其提供长度为4的列表a->b->c->d
调用递归变量(&p)其中p=a
first->next
和rest
都将是b
,因此指向一个三元素列表b->c->d
,因此(根据我们的假设)
recursiverse(&rest)
将正确反转此列表。之后
调用时,rest
已将值(从b
更改为d
),现在指向此反向列表d->c->b
first->next
仍然是调用之前相同的指针b
,因此现在指向列表的末尾。
因此,first->next->next=first
将first
附加到该反向列表的末尾,然后该列表变为d->c->b->a
)
由于first
现在是列表的末尾,我们现在需要first->next=NULL
。最后一步是更改*head\u ref
(从a
更改为d
),因此,从recursiverse(&p)
返回后,p
将从a
更改为列表的新head,d
这表明,只要函数对n元素正确工作
列表,它可以正确地用于n+1元素列表。基本情况很简单,
所以我们已经证明它适用于所有列表
现在,为什么不在调试器中看到rest
正在更改值?因为
其值仅通过函数调用更改
recursiverse(&rest)
。在你递归调用之前
recursiverse
它有一个值,从它返回后,它有一个值
另一方面,当您在每个函数调用中进出时,您看不到它的变化。更改其值的赋值实际上是函数返回之前的最后一个(*head\u ref=rest
),但是在这个堆栈帧中调用受让人的是head\u ref
,而不是rest
(在调用方的堆栈帧中调用)
这就是我上面提到的“名称相同,但值不同”的混淆
void recursiveReverse(struct node** head_ref)
{
struct node* first;
struct node* rest;
/* empty list */
if (*head_ref == NULL)
return;
/* suppose first = {1, 2, 3}, rest = {2, 3} */
first = *head_ref;
rest = first->next;
/* List has only one node */
if (rest == NULL)
return;
/* reverse the rest list and put the first element at the end */
recursiveReverse(&rest);
first->next->next = first;
/* tricky step -- see the diagram */
first->next = NULL;
/* fix the head pointer */
*head_ref = rest;
}