【问题标题】:replacing recursive DFS tree traversal with a stack?用堆栈替换递归 DFS 树遍历?
【发布时间】:2025-11-29 03:05:01
【问题描述】:

众所周知,可以使用递归(例如在 Python 中)通过 DFS(预排序)遍历树:

def DFS(node):
    if node==None:
       return 0
    valueLeft=DFS(node.left)
    valueRight=DFS(node.right)
    return max(valueLeft,valueRight)+1

如果我们调用DFS(root),这段代码显然会从根开始计算树的高度。但是有一个问题(......在某些机器中,python 的递归堆栈大小有限,所以这段代码溢出检查在 leetcode 中抱怨......)。

出于好奇以及解决 leetcode 问题,如何将上述带值返回的两分支递归转换为使用堆栈的迭代调用?我想这是可能的,因为递归是使用堆栈实现的,无论如何。从第一天开始,我在课堂上就被告知了,但从未见过这样的代码,尤其是对于具有返回值的多分支递归......

我会提前感谢任何人的帮助。

【问题讨论】:

    标签: python algorithm recursion stack


    【解决方案1】:

    您可以为堆栈使用堆空间 - 基本上维护自己的堆栈:

    def dfs2(root):
      stack = [(root,0)]
      depth = 0
      while stack:
        node, d = stack.pop()
        if node == None:
          depth = max(depth,d)
        else:
          stack.push( (node.left, d+1) )
          stack.push( (node.right, d+1) )
      return depth
    

    【讨论】:

    • 这实际上是错误的,虽然是无限的。由于您没有标记访问过的节点
    • 一个节点可以有多个父节点吗?如果是这样,它就不是一棵树。你能举一个while循环永远不会终止的例子吗?
    • @bozeng 如果这个while 是无限的,那么你的 DFS 也是无限的!
    • 我明白了,所以这绝不是递归。在递归中,您需要在访问其子项后“返回父项”。然后它将再次将其孩子推回堆栈。形成一个循环。这个答案不是递归。它只是计算树高的非递归版本
    • 我想要的是一个真正的带堆栈的递归版本。如果我计算别的东西。或要求按顺序访问。这东西行不通。
    【解决方案2】:

    查看http://stromberg.dnsalias.org/svn/treap/trunk/m4_treap.m4。它在 iterator_macro 中没有递归地进行树遍历。

    不要让文件扩展名欺骗您 - 它是 m4,但它是 m4 预处理的 python,因此我可以将相同的宏用于多种非递归遍历 - 特别是 iterkeys、itervalues 和 iteritems,而无需大量样板在 m4 中。

    最终,m4 被省去,python 解释器只看到 python 代码。

    在同一个模块中,reverse_iterator 也进行了非递归遍历。如果你想忽略其他人的 m4-ness,这可能是一个更简单的地方。

    您还可以使用 sys.setrecursionlimit(size) 增加 python 解释器的堆栈深度,尽管我认为 pypy 几乎忽略了这一点。 CPython 尊重它。

    【讨论】: