Recursion 合并两个排序列表问题中的递归可视化问题

Recursion 合并两个排序列表问题中的递归可视化问题,recursion,data-structures,linked-list,Recursion,Data Structures,Linked List,问题: 解决方案: 我的理解是,由于L2将为空,我们最终命中了基本情况并返回4 我只是有点困惑,如果有人能描述调用堆栈开始弹出时的流程,以及我们最终如何得到一个合并列表,我会很感激。我想,基本情况返回[4],而不是一个数字。否则,类型将是错误的。但我不会说JavaScript,所以我可能错了。不管怎样,我认为这对这个问题不重要 我不知道它有多大帮助,但是你可以把递归看作是做一件向下的事,另一件向上的事,当你向上时,你必须做的是存储在堆栈上的东西。(使用尾部递归,可以避免第二步,因此这通常是更好的

问题:

解决方案:

我的理解是,由于L2将为空,我们最终命中了基本情况并返回4


我只是有点困惑,如果有人能描述调用堆栈开始弹出时的流程,以及我们最终如何得到一个合并列表,我会很感激。我想,基本情况返回[4],而不是一个数字。否则,类型将是错误的。但我不会说JavaScript,所以我可能错了。不管怎样,我认为这对这个问题不重要

我不知道它有多大帮助,但是你可以把递归看作是做一件向下的事,另一件向上的事,当你向上时,你必须做的是存储在堆栈上的东西。(使用尾部递归,可以避免第二步,因此这通常是更好的方法,但并不总是容易实现)

以这种方式合并列表时,首先从列表
[1,2,4]
[1,3,4]
开始,然后选择要在哪个分支上递归。调用堆栈会记住每一级列表的前端在局部变量中的位置,并通过保存指令ponter来记住所采用的分支

state: [1, 2, 4], [1, 3, 4]. => not 1 < 1 so go right, remember (right)
state: [1, 2, 4], [3, 4].    => 1 < 1 so go left, remember      (left)
state: [2, 4], [3, 4].       => 2 < 3 so go left, remember.     (left)
state: [4], [3, 4]           => not 4 < 3 so go right, remember (right)
state: [4], [4]              => not 4 < 4 so go right, remember (right)
state: [4], []               => base case, return [4]
因此,我们从基本情况的返回值
[4]
开始,我们在右分支中处于状态1。在这里,我们将返回的列表
[4]
放入
l2
的下一个值中,因此
l2
[4]
更改为
[4,4]
,然后返回

state 1: [4], [4] => return [4, 4]
state 2: [4], [3] => (right)
state 3: [2], [3] => (left)
state 4: [1], [3] => (left)
state 5: [1], [1] => (right)
这就把我们带到了状态2,在这里我们处于右分支,返回的列表是
[4,4]
,而在
l2
中返回的列表是3。实际上,由于我们在返回之前修改了
l2
,它已经有了剩余的列表,所以它实际上是
[3,4,4]
。这并不重要,因为我们将
l2.next
设置为
[4,4]
,所以现在肯定是这样。我们把它还给你

state 2: [4], [3] => return [3, 4, 4]
state 3: [2], [3] => (left)
state 4: [1], [3] => (left)
state 5: [1], [1] => (right)
我们进入状态3,我们在左分支。我们从递归调用中获得了列表
[3,4,4]
(它也是
l2
,因为它是
l2。接下来我们修改了
,状态中的
[3]
应该是
[3,4,4]
。这就是
l2
现在所持有的内容)。由于我们在左边的分支中,我们更新了
l1
。下一步
,因此
l1
变成
[2,3,4,4]
(2是它当前的头,其余是我们返回的头)。同样,
l1
实际上包含了更多的值,我们在递归过程中从未对其进行过修改,但重要的只是头部。在我们写入
l1之后。下一步
之前的值在任何情况下都会消失。所以现在如果你想严格要求的话,
l1=[2,3,4,4]
l2=[3,4,4]
,但唯一重要的是当我们向上移动时他们的头是什么,我们从递归返回
[2,3,4,4]

state 3: [2], [3] => [2, 3, 4, 4]
state 4: [1], [3] => (left)
state 5: [1], [1] => (right)
在状态4中,
l1
l2
的头分别是1和3,我们从递归中得到了
[2,3,4,4]
。实际值是
l1=[1,2,3,4,4]
l2=[3,4,4]
,但同样,只有头部才重要。我们在一个左分支中,所以我们应该将返回值,
[2,3,4,4]
放在
l1
中。下一个
,它将为我们提供
[1,2,3,4,4]
(这是我们已经拥有的,但这并不重要……)

在状态5中,我们向右转,因此我们应该将返回值添加到
l2。下一步
,它将
l2
更改为
[1,1,2,3,4]
,因为头部是1。这是我们返回的列表

state 1: [4], [4] => return [4, 4]
state 2: [4], [3] => (right)
state 3: [2], [3] => (left)
state 4: [1], [3] => (left)
state 5: [1], [1] => (right)
这个实现中令人困惑的是,当您从递归返回时,您正在修改列表。当我们返回时,
l1
l2
指的是修改列表的头部。我们只需要头部,递归就可以工作,但我们有完整的列表。这并没有什么错,但是在调用merge之后,列表应该被认为是丢失的。你将改变它们作为副作用。如果在从递归返回时构建新链接,则可以避免这种情况。如果在累加器前面加上一个累加器,然后在最后反向加上累加器,这也会使尾部递归版本变得更容易


我不知道这是否有帮助,但我认为我最好在早上一大早就这样做…

谢谢您的详细回复!我希望它至少有一点用处。是的,看起来很有用,但相当复杂,哈哈,特别是对于那些喜欢迭代而不是递归的人来说,如果你想要的话,得到一个迭代合并是非常容易的。它不会比这个非尾部递归版本复杂得多,而且速度肯定会更快。
state 4: [1], [3] => [1, 2, 3, 4, 4]
state 5: [1], [1] => (right)