反转链接列表递归

时间:2014-01-15 06:44:44

标签: c++ recursion linked-list

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

我应该反转一个链表。和hackerrank一样,这段代码运行正常。

代码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 输出:分段错误

但如果我做了以下更改,它会以某种方式工作,但不会显示第一个数字。

CODE 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?

7 个答案:

答案 0 :(得分:1)

在你的代码中 在案例B中:

void rev(Node* head){
if(head==NULL) return;
head=head->next;
rev(head);
cout<<head->data<<endl;}

让我们看一下Stack Segment的递归。

NODE1 -> NODE2->NULL NODE1 and NODE2为结构对象。

你的功能是:

  1. 致电rev(NODE1)
  2. 检查是否为NULL
  3. 指向next NODE i.e. NODE2

  4. 致电rev(NODE2)

  5. 检查是否为空
  6. 指向next NODE i.e. NULL

  7. 致电rev(NULL)

  8. 检查是否为空
  9. 指针将与head = NULL
  10. 一起返回

    和 这是错误,即您正在访问head->data以获取NULL值。

    希望这会帮助你。

答案 1 :(得分:0)

当然,如果 B ,您无法访问内存。您的指针指向您未分配的地址(NULL)。 顺便说一句,我建议制作双链表。更方便。

如果 C ,您为函数void rev (Node* head)指定一个指向列表的指针,并跳过第二行head=head->next;,这是您的第一个元素。因此,它不会显示在std::cout的输出中。

使用案例A,你很好。

答案 2 :(得分:0)

链接列表迭代器只能继续到链表的末尾。

所有链接列表迭代器代码必须包含循环不变量 - 测试确保如果它接下来取消引用,则下一个有效。如果不是,它就会停止。这通常采用以下形式:

if(head==NULL) return;

while(head!=NULL)

if(head->next != NULL) recurse(head->next)

如果 NULL,那么head->next(或head->datahead->anything

是不安全的

更改的效果是绕过循环不变量,并查看head->next内部,而不检查它是否为NULL

具体来说,您在检查head->data是否为空之前访问head,以及 BOOM!

看起来你在/'我应该切断蓝线还是黄线'/领域。玩得开心。


虽然你没有使用循环,但递归也是一种迭代形式。

答案 3 :(得分:0)

让我为您重写代码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的空指针,因此您必须阅读用于执行此赋值的null指针。

代码C 中,if (head->next==NULL) return;行阻止您使用该行下方的代码,以防下一个元素为空,因此不会打印当前头部。

答案 4 :(得分:0)

您的链表结构是2-> 1-> 4-> 5-> NULL

代码B:

在递归时,当head具有data = 5时,head = head-&gt; next使head = NULL,所以下一次调用rev()会立即返回head = NULL,现在你尝试访问head-&gt ;与NULL->数据相同的数据,结果是分段错误。

答案 5 :(得分:0)

不是你问题的直接答案,但是你是否有单向或双向链表,如果你想反转该列表,那么建议同时保持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;
}

答案 6 :(得分:0)

代码A和代码B不相似。在代码A中,您将head->next作为rev的参数传递而不更改指针head,但在代码B中,您更改了指针head。因此,调用cout<<head->data

时,结果会有所不同

您的链表样本可以显示为:2-&gt; 1-&gt; 4-&gt; 5-&gt; 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的三次调用,因此只打印了三个元素。