在二叉树中查找指定节点的路径(Python)

时间:2018-03-12 02:56:56

标签: python binary-tree depth-first-search tree-traversal

我在计算从根到二叉树中指定节点的路径时遇到了麻烦(这特别是关于这个问题的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]

你可以看到我没有得到预期。我确定它与我的遍历算法有关,但我无法弄清楚我哪里出错了。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:5)

缺少的4是由于您永远不会附加它而引起的。在你的成功案例中:

if root.val == k:
    return l

...你需要这个:

if root.val == k:
    l.append(root.val)
    return l

额外35是由于总是在中间案例中附加值的事实造成的,即使对于您要去的节点也是如此回溯。

如果任何一个递归调用返回非空列表,你可以通过仅附加它来修复它,但当然你会让节点乱序。最简单的解决方法是故意使节点无序:

# 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,它的仍然值得学习如何以尾调用的方式做事,只是为了你自己的理解(如果你曾经写过代码)另一种语言,当然)。但是先学会如何做更简单的版本,然后再尝试颠倒它。