我在计算从根到二叉树中指定节点的路径时遇到了麻烦(这特别是关于这个问题的Python解决方案)。
这是一个例子。给定下面的二叉树,如果我指定值为4的节点,我想返回[1,2,4]。如果我指定值为5的节点,我想返回[1,2,5]。
1
/ \
2 3
/ \
4 5
这是我尝试过的解决方案。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def path(root, k, l=[]):
if not root:
return []
if root.val == k:
return l
# Pre-order traversal: Visit root, then left, then right.
l.append(root.val)
path(root.left, k, l)
path(root.right, k, l)
return l
现在如果我运行
>>> a = TreeNode(1)
>>> b = TreeNode(2)
>>> c = TreeNode(3)
>>> d = TreeNode(4)
>>> e = TreeNode(5)
>>> a.left = b
>>> a.right = c
>>> b.left = d
>>> b.right = e
>>> path(a, 4) # should be [1, 2, 4]
[1, 2, 5, 3]
你可以看到我没有得到预期。我确定它与我的遍历算法有关,但我无法弄清楚我哪里出错了。非常感谢任何帮助。
答案 0 :(得分:5)
缺少的4
是由于您永远不会附加它而引起的。在你的成功案例中:
if root.val == k:
return l
...你需要这个:
if root.val == k:
l.append(root.val)
return l
额外3
和5
是由于总是在中间案例中附加值的事实造成的,即使对于您要去的节点也是如此回溯。
如果任何一个递归调用返回非空列表,你可以通过仅附加它来修复它,但当然你会让节点乱序。最简单的解决方法是故意使节点无序:
# Pre-order traversal: Visit root, then left, then right.
if path(root.left, k, l) or path(root.right, k, l):
l.append(root.val)
...然后在结尾处反转列表,例如,在包装函数中:
def path2(root, k):
return list(reversed(path(root, k)))
但是,您的代码中仍然存在一个问题,就在这里:
def path(root, k, l=[]):
[]
的{{1}}创建l
的默认值,执行def
,然后在每次调用时重复使用。这意味着path2(a, 4)
将在第一次[1, 2, 4]
时返回正确的答案,但是当您第二次调用它时,它会继续追加到相同的列表并返回[1, 2, 4, 1, 2, 4]
有一些惯用的方法,但在我们的情况下,由于我们已经使用了path2
包装函数,我们不妨在那里修复它:
def path2(root, k):
return list(reversed(path(root, k, [])))
...然后摆脱path
上的默认值。
但是,您可能需要考虑从易于理解的版本开始:
def path(root, k):
if not root:
return []
if root.val == k:
return [root.val]
res = path(root.left, k)
if res:
return [root.val] + res
res = path(root.right, k)
if res:
return [root.val] + res
return []
(你可以把它缩短一点;我不遗余力地在这里明确一切。)
对于许多递归问题,反转它们并传递累加器是一个重要的优化,因为尾部调用消除可以从堆栈中删除其中一个分支。即使Python不做TCE,它的仍然值得学习如何以尾调用的方式做事,只是为了你自己的理解(如果你曾经写过代码)另一种语言,当然)。但是先学会如何做更简单的版本,然后再尝试颠倒它。