我认为使用更长的源列表实际上更容易可视化。如果您使用[0, 1, 2]
,您会看到递归调用反复切断列表中的最后一项。也就是说,recusion 会构建一堆递归调用,如下所示:
genSubsets([0,1,2])
genSubsets([0,1])
genSubsets([0])
genSubsets([])
在这一点上,它达到了递归算法的“基本情况”。对于此函数,基本情况是作为参数给出的列表为空时。命中基本情况意味着它返回一个包含空列表的列表[[]]
。以下是堆栈返回时的样子:
genSubsets([0,1,2])
genSubsets([0,1])
genSubsets([0]) <- gets [[]] returned to it
这样返回值就会回到上一级,并保存在smaller
变量中。变量extra
被分配为一个切片,只包括列表的最后一项,在这种情况下是整个内容,[0]
.
现在,循环遍历 中的值smaller
,并将它们的连接与extra
to相加new
。由于smaller
(空列表)中只有一个值,因此new
最终也只有一个值,[]+[0]
即[0]
. 我认为这是您在某个时候打印出来的值。
smaller
然后最后一条语句返回and的串联new
,所以返回值为[[],[0]]
。堆栈的另一个视图:
genSubsets([0,1,2])
genSubsets([0,1]) <- gets [[],[0]] returned to it
返回值被smaller
再次分配给extra
is [1]
,并且循环再次发生。这一次,new
得到两个值,[1]
和[0,1]
。它们再次连接到末尾,smaller
返回值为[[],[0],[1],[0,1]]
. 最后一个堆栈视图:
genSubsets([0,1,2]) <- gets [[],[0],[1],[0,1]] returned to it
同样的事情再次发生,这次2
在到目前为止找到的每个项目的末尾添加了 s。new
结束为[[2],[0,2],[1,2],[0,1,2]]
.
最终返回值为[[],[0],[1],[0,1],[2],[0,2],[1,2],[0,1,2]]