【问题标题】:C++ Reverse Linked List Recursively Why it works?C++ 递归反向链表为什么有效?
【发布时间】:2019-06-06 07:55:09
【问题描述】:

这是问题所在:https://leetcode.com/problems/reverse-linked-list/

我知道解决方案(而且它在互联网上到处都是复制粘贴的),但我不明白 其中的一部分是如何工作的... 所以:

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

   ListNode* reverseList(ListNode* head) {
        if (!head || !(head -> next)) {
            return head;
        }
        ListNode* node = reverseList(head -> next);
        head -> next -> next = head;
        head -> next = NULL;
        return node;
    }

这行得通。

现在我通过调试器得到它并看到我不明白的东西......

假设我们有一个链表:

1->2->3->NULL

1 ) 我们已将最后一个 3->Null 部分传递给 ListNode* node = reverseList(head->next); 它确实是return head;,现在是ListNode* node = 3->NULL;

好的,node == 3->NULLhead == 2->3->NULL

2) 让我们转到head->next->next = head

现在head == 2->3->2->3... 但是为什么 node == 3->2->3->2... ???

它们是如何连接的?我在这里完全糊涂了。

3) 在下一行head -> next = NULL 我们可以看到它再次影响headnode

如您所见,我不明白nodehead 之间的联系。 所以我不明白它是如何工作的,我觉得我不明白这些链接列表一般是如何工作的。 也许有人可以帮助我?欣赏它。

【问题讨论】:

  • "现在head == 2->3->2->3..。但是为什么node == 3->2->3->2... ???"反转函数使用 chaining 反转列表。注意 recursive 调用 ListNode* node = reverseList(head -> next); 在函数中的位置。之后returns——会发生什么?每次递归调用之后都会对指针进行哪两次重新分配?
  • @DavidC.Rankin,嗨! "ListNode* 节点 = reverseList(head -> next);"据我了解,返回node == 3->null(您可以在调试器的屏幕截图中看到它)。并将其返回到堆栈具有node == 2->3->null 的程序状态; “每次递归调用后,都会对指针进行哪两次重新分配?”你的意思是这些行吗:head -> next -> next = head; head -> next = NULL 但它们是在head 上制作的,而不是在node 上制作的。那么为什么node 也会受到影响呢?这是我的问题。
  • 递归函数 "Unwind" 返回。按照递归 "Winds-In" 的路径直到 head == NULL | head->next == NULL,然后将返回跟踪为递归 "Unwinds"。 (这就是你必须如何看待递归)。在第一个 return 控制 "unwinds" (返回到)之前的函数调用,其中两个赋值是在该级别递归 returns 之前进行的......(其中 “unwinds”(返回)之前的调用)。这就像从洋葱的中心开始,然后从中心向外工作,然后一层一层地……用铅笔和纸做一张桌子——这很有帮助:)
  • @DavidC.Rankin,感谢您的支持,但我想我以前就知道这一切,这对我的问题没有帮助。也许你理解的更深,但你的解释不是我理解的水平,或者你不理解。我不知道哪个是正确的,更喜欢先思考(你比我聪明,只是无法向我智商的人解释)......
  • 请注意,如果列表不太长,它虽然有效,但效率低下。迭代解决方案更可取。

标签: c++ recursion linked-list


【解决方案1】:

不幸的是,我没有足够的声誉来发表评论,所以我将发布答案并希望它对您有所帮助。我将使用绘画图片使事情更容易可视化。让我们开始吧:

假设我们有列表 1->2->3-> 所以它的开头是这样的:

现在我们启动这个函数,它一直运行到第一个递归,它从一个新的头开始,我们有以下情况:

然后函数再次运行,我们得到以下结果:

然后我们从一个新的 head (3) 开始,但这里最后一个函数返回 head,因为满足 if 条件 head->next == NULL。所以我们有:

然后我们跟随函数直到最后改变head->next->next = headhead->next = NULL所以我们有:

然后函数返回节点,我们返回到原来的调用。然后我们执行相同的步骤,最终得到:

函数最后返回节点,所以我们最终得到:

希望这对您有所帮助,并对答案质量不佳感到抱歉,但我发现在可视化递归时更容易解决递归问题,最简单的可视化方法是绘图。

【讨论】:

  • 嗨,@Valgrim!谢谢你花这么多时间画图)但我以前明白这一点。让我感到困惑的是,当我们更改headhead->next-next = headhead->next = NULL)时,它也会影响指针node。问题是node 是一个指针,并且“指向”与head->next 相同的地址。
  • 影响是什么意思。正如你所说,它是一个指向某物的指针。如果你改变那个东西的值,只要它指向同一个东西,它就不会受到影响。如果您更改head->next,您不会影响node 指针。您将head 的指针的值更改为等于其他值。 node 指针不受影响。
  • 你确实影响它,看截图。
  • 嗯,我在 C++ 上运行代码,它运行得很好,所以我不明白你的问题出在哪里。无论如何,我希望我能帮助你,并希望你能弄清楚。
  • 为创意讲座点赞!
【解决方案2】:

好的,可能我自己已经找到了答案。 问题又来了:

        ListNode* node = reverseList(head -> next);
        head -> next -> next = head;
        head -> next = NULL;
        return node;

我的问题是为什么更改 head 的字符串 2 和 3 也会影响(更改)node

这是因为node 是一个指针,它“指向”与head->next 相同的内存地址。

您实际上可以在第一个屏幕截图中看到这一点:

0x100300020是node的地址

0x100300020是head->next的地址

它们指向内存中的相同地址。

所以当我们这样做时:

        head -> next -> next = head;
        head -> next = NULL;

我们还将其更改为node 指向的地址中的“存储的东西”。

【讨论】:

    猜你喜欢
    • 2021-04-15
    • 1970-01-01
    • 2011-12-19
    • 2013-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多