You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

LeetCode路径总和II问题:递归中列表传递与修改机制疑惑

嘿,我来帮你把这个递归里列表传递的逻辑掰扯清楚!你疑惑的核心点其实是对Python中列表的对象引用传递理解得有点偏差,咱们结合你的代码一步步说:

先搞懂Python里列表的传递本质

Python里的变量传递是传对象引用,但这和你以为的“修改深层列表会影响浅层”不是一回事——关键看你是修改原列表对象,还是创建了新的列表对象:

  • 如果在函数里对列表做append()、pop()这类操作,是直接修改原列表对象,上层函数的列表会跟着变;
  • 但如果做ls + [root.val]这种操作,本质是生成了一个全新的列表对象(把原ls的元素和新元素合并),这个新列表和原来的ls没有任何关系。
结合你的代码分析为什么浅层ls没被修改

看你递归调用的代码:

self.dfs(root.left, sum-root.val, ls+[root.val], res)

这里你传递给下一层递归的不是原ls的引用,而是ls+[root.val]生成的新列表。所以每一层递归拿到的ls都是独立的新对象:

  • 比如第一层递归的ls是空列表,调用左子树时传递的是[] + [root.val],也就是[root.val]这个新列表;
  • 下一层递归再调用它的左子树时,又会生成[root.val, left_child.val]这个新列表,以此类推。

当你在深层递归里执行ls.append(root.val)时,修改的只是当前层的这个新列表,上层的ls还是原来的状态——因为它们根本不是同一个对象,自然不会被影响。而你看到print(ls)输出的是当前层添加后的列表,那是因为这个ls是当前层独有的新列表,和上层的ls没关系。

对比另一种常见写法(修改原列表+回溯)

如果我们换一种写法,直接传递原ls的引用,那情况就不一样了,比如:

def dfs(self, root, sum, ls, res):
    # 先把当前节点加入列表
    ls.append(root.val)
    # 找到符合条件的叶子节点
    if not root.left and not root.right and sum == root.val:
        res.append(ls.copy())  # 必须存副本!否则后续修改会影响res里的内容
        print(ls)
    # 递归左子树
    if root.left:
        self.dfs(root.left, sum-root.val, ls, res)
    # 递归右子树
    if root.right:
        self.dfs(root.right, sum-root.val, ls, res)
    # 回溯:移除当前节点,恢复上层列表的状态
    ls.pop()

这种写法里,所有递归层共享同一个ls对象,所以append会修改原列表。这时候就必须在递归返回后执行ls.pop(),把当前节点从列表里移除——也就是回溯,这样上层递归才能继续处理其他分支。同时,存结果的时候要存ls.copy(),不然后续的pop会把res里的列表也改了。

你的代码为什么能正确运行?

你的写法其实是另一种合法思路:通过每次递归创建新列表,让每一层的路径都是独立的,不需要回溯。当找到符合条件的路径时,直接把这个新列表加入res即可,后续的递归操作不会影响它,因为大家操作的都是各自的列表对象。

两种写法各有优劣:你的写法更直观,不需要考虑回溯,但每次创建新列表会有一点点额外的内存开销;回溯的写法更节省内存,但需要注意状态的恢复。

内容的提问来源于stack exchange,提问作者Rahil Rehan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:47:51