反转链表递归
reversing a Linked List Recursion
struct Node
{
int data;
struct Node *next;
}
我应该反转链表。与黑客兰克一样,此代码工作正常。
代码 A
void rev(Node* head){
if(head==NULL) return;
rev(head->next);
cout<<head->data<<endl;
}
输入: 2 1 4 5输出: 5 4 1 2
但是如果我进行以下小更改,它就会中断,即我得到分段错误!
代码 B
void rev(Node* head){
if(head==NULL) return;
head=head->next;
rev(head);
cout<<head->data<<endl;
}
输入: 2 1 4 5输出:分段错误
但是,如果我进行以下更改,它会以某种方式工作,但不显示第一个数字。
代码 C
void rev(Node* head){
if(head->next==NULL) return;
head=head->next;
rev(head);
cout<<head->data<<endl;
}
输入: 2 1 4 5输出: 5 4 1
问题:代码 A 和代码 B 大多相似。什么是破坏代码 B?
在你的代码中在案例 B 中:
void rev(Node* head){
if(head==NULL) return;
head=head->next;
rev(head);
cout<<head->data<<endl;}
让我们看一下递归的堆栈段。
让我们NODE1 -> NODE2->NULL
其中NODE1 and NODE2
是结构对象。
你的函数做什么是:
- 致电
rev(NODE1)
- 检查它是否为空
指向
next NODE i.e. NODE2
致电
rev(NODE2)
- 检查它是否为空
指向
next NODE i.e. NULL
致电
rev(NULL)
- 检查它是否为空
- 指针将返回
head = NULL
和这是错误,即您正在访问 NULL 值的head->data
。
希望这会对您有所帮助。
好吧,当然,在情况 B 中,您无法访问内存。指针指向未分配的地址 (NULL)。顺便说一下,我建议制作一个双链表。更方便。
在 C 的情况下,你给函数void rev (Node* head)
一个指向列表的指针,并跳过第二行head=head->next;
这是你的第一个元素。因此,它不会显示在 std::cout
的输出中。
用例 A,你没事。
链表迭代器只能继续到链表的末尾。
所有链表迭代器代码都必须包含一个循环不变量 - 测试确保如果它接下来要取消引用,下一个是有效的。 如果不是,它将停止。 这通常采用以下形式:
if(head==NULL) return;
或
while(head!=NULL)
或
if(head->next != NULL) recurse(head->next)
如果头部是NULL
,那么做head->next
是不安全的(或head->data
或head->anything
)
更改的效果是绕过循环不变量,并查看head->next
内部而不检查它是否NULL
。
具体来说,您先访问head->data
,然后再检查head
是否为空,然后 BOOM!。
看起来你在/'我应该剪蓝线还是黄线'/领域。 玩得愉快。
虽然您没有使用循环,但递归也是一种迭代形式。
让我为您重写代码 A:
void rev(Node* head){
if(head!=NULL)
{
rev(head->next); //recursive call with pointer to next!
cout<<head->data<<endl; //write after recursive call (reversed)
}
}
代码 B 可以用相同的方式重写:
void rev(Node* head){
if(head!=NULL)
{
head=head->next;
//head becomes next, but what if next is null,
//you try to read next, which is a null pointer here == segmentation fault
rev(head);
cout<<head->data<<endl;
}
}
所以基本上代码 A 不读取空指针,但在代码 B 中,您将空指针分配给head
因此您必须读取空指针才能执行此分配。
在代码 C 中,行 if (head->next==NULL) return;
阻止您在下一个元素为 null 的情况下使用该行下面的代码,因此不会打印当前标头。
您的链表结构为 2->1->4->5->NULL
代码 B:
在递归时,当 head 有 data=5,head=head->next 使 head=NULL ,所以下一次调用 rev() 立即返回 head=NULL,现在你尝试访问与 NULL->data 相同的 head->data 结果是分割错误。
不是对你的问题的直接回答,但无论你有一个单向还是双向链表,如果你想反转该列表,那么最好同时维护一个 Head 指针和一个 Tail 指针:
要反转单向链表,请调用Reverse()
:
void Swap(Node* pCurr,Node* pNext)
{
if (pNext != NULL)
{
Swap(pNext,pNext->pNext);
pNext->pNext = pCurr;
}
}
void Reverse()
{
if (pHead != NULL)
{
Swap(pHead,pHead->pNext);
pHead->pNext = NULL;
}
Node* pTemp = pHead;
pHead = pTail;
pTail = pTemp;
}
要反转双向链表,请调用Reverse()
:
void Swap(Node* pCurr)
{
if (pCurr->pNext != NULL)
{
Swap(pCurr->pNext);
pCurr->pNext->pNext = pCurr;
}
pCurr->pPrev = pCurr->pNext;
}
void Reverse()
{
if (pHead != NULL)
{
Swap(pHead);
pHead->pNext = NULL;
}
Node* pTemp = pHead;
pHead = pTail;
pTail = pTemp;
}
代码 A 和代码 B 并不相似。在代码 A 中,将 head->next
作为 rev
的参数传递而不更改指针head
,但在代码 B 中,将指针head
更改为 。因此,它们在调用cout<<head->data
时会产生不同的结果
您的链表样本可以可视化为:2->1->4->5->NULL
您的评估可能被评估为(相同范围的相同缩进):
在代码 A 中:
head: 2, call rev(head) //your initial call
head: 2, check head==NULL false
head: 2, call rev(head->next)
head: 1, check head==NULL false
head: 1, call rev(head->next)
head: 4, check head==NULL false
head: 4, call rev(head->next)
head: 5, check head==NULL false
head: 5, call rev(head->next)
head==NULL, check head==NULL true
return
head: 5, call cout<<head->data<<endl;
head: 4, call cout<<head->data<<endl;
head: 1, call cout<<head->data<<endl;
head: 2, call cout<<head->data<<endl;
return
但在代码 B 中:
head: 2, call rev(head) //your initial call
head: 2, check head==NULL false
head: 2, head = head->next
head: 1, call rev(head)
head: 1, check head==NULL false
head: 1, head = head->next
head: 4, call rev(head)
head: 4, check head==NULL false
head: 5, head = head->next
head: 5, call rev(head)
head: 5, check head==NULL false
head: 5, head = head->next
head==NULL, call rev(head)
head==NULL, check head==NULL true
return
head: NULL, call cout<<head->data<<endl; //segmentation fault here
否则,在代码 C 中:
head: 2, call rev(head) //your initial call
head: 2, check head->next==NULL false
head: 2, head = head->next
head: 1, call rev(head)
head: 1, check head->next==NULL false
head: 1, head = head->next
head: 4, call rev(head)
head: 4, check head->next==NULL false
head: 5, head = head->next
head: 5, call rev(head)
head: 5, check head->next==NULL true
return
head: 5, call cout<<head->data<<endl;
head: 4, call cout<<head->data<<endl;
head: 1, call cout<<head->data<<endl;
return
看到有三个调用cout
,所以只打印了三个元素。
- 使用双指针和递归反转链表
- 使用类进行实现时,递归打印C++中的链表
- 以递归方式从链表中删除节点
- 重载 == 以递归方式比较两个链表
- 链表中的递归长度函数实现
- 递归解决方案,用于显示线性链表数组
- 使用递归从尾部开始反转链表
- C++ 循环链表的递归基本情况
- 编写递归算法以从链表中删除元素.编写递归算法以将元素添加到链表中
- 尝试使用递归和指针到指针反转链表,但反转函数没有给出预期的正确输出
- 以C++递归方式对链表中的元素求和
- 使用递归的二维链表复制构造函数
- 反向链表的递归函数(代码段说明)
- 递归复制链表
- 链表的递归析构函数
- 不太清楚为什么我的递归链表删除函数有效?我很想解释一下
- C/C++ 到 Java 递归反转链表的翻译
- 使用 STL 反转递归链表
- 编译器错误递归链表
- 如何在C++中调用递归链表遍历函数