【问题标题】:Binary Tree Traversal Using Recursion Explanation (python)使用递归解释的二叉树遍历(python)
【发布时间】:2021-05-04 08:18:19
【问题描述】:

我目前正在研究二叉树。我遇到了这个非常有效的遍历树的代码(在示例中这是一个按顺序遍历)。它使用递归,这是我理解的一个概念。 但是,我似乎无法理解这实际上是如何工作的。我主要感到困惑的是它是如何每次都出现在列表中的,所以 start.left 并不总是相同的数字。 有人可以逐步说明这实际上是如何遍历树的。提前致谢

已编辑以增加问题的清晰度:

  1. 我知道如果 start 不是 None,那么 start.left 会被添加到同一函数的递归调用中。
  2. 每次递归时,都会将变量遍历分配给函数的返回值。
  3. 当 start 最终为 None(遍历已完成)时,函数返回遍历元素,并继续该过程。

我的困惑是,我似乎无法找到代码在哪里,“知道”它遍历到 4,现在要返回的下一个元素是 2。机制在哪里,现在停止在 2 并返回它,等等?

class Node():

def __init__(self, data):
    self.data = data
    self.left = None
    self.right = None
    
    
def __str__(self):
    return str(self.data)
    

class BinaryTree(object):
def __init__(self, root):
    self.root = Node(root)          
                    
                        
def inorder_print(self, start, traversal):
    """Left -> root -> right"""
    if start:
        traversal = self.inorder_print(start.left, traversal)
        traversal += (str(start.data) + "-")
        traversal = self.inorder_print(start.right, traversal)
        

    return traversal
    
    
    
tree = BinaryTree(1)
tree.root.left = Node(2)
tree.root.right = Node(3) 
tree.root.left.left = Node(4)
tree.root.left.right = Node(5)
tree.root.right.left = Node(6)
tree.root.right.right = Node(7)
tree.root.right.right.right = Node(8)

print(tree.inorder_print(tree.root, ""))

【问题讨论】:

  • 它在执行return时上升。这是一个很模糊的问题。您能否尽可能一步一步地添加解释,然后准确指出在此遍历中您不了解下一步的确切位置?
  • 那里有很多中序遍历的解释。我建议只用谷歌搜索它
  • 我想你会发现this Q&A 很有帮助,this Q&A 也是如此。

标签: python recursion binary-tree


【解决方案1】:

了解算法所采用路径的一个有用工具是向其添加日志记录。

    def inorder_print(self, start, traversal, indent=""):
        """Left -> root -> right"""
        if start:
            print(f"{indent} {start.data} <- '{traversal}'")
            traversal = self.inorder_print(start.left, traversal, indent+" ")
            traversal += (str(start.data) + "-")
            print(f"{indent} {start.data} -- '{traversal}'")
            traversal = self.inorder_print(start.right, traversal, indent+" ")
            print(f"{indent} {start.data} -> '{traversal}'")

        return traversal

允许我们可视化树以及每个节点添加到遍历中的顺序:

 1 <- ''
  2 <- ''
   4 <- ''
   4 -- '4-'
   4 -> '4-'
  2 -- '4-2-'
   5 <- '4-2-'
   5 -- '4-2-5-'
   5 -> '4-2-5-'
  2 -> '4-2-5-'
 1 -- '4-2-5-1-'
  3 <- '4-2-5-1-'
   6 <- '4-2-5-1-'
   6 -- '4-2-5-1-6-'
   6 -> '4-2-5-1-6-'
  3 -- '4-2-5-1-6-3-'
   7 <- '4-2-5-1-6-3-'
   7 -- '4-2-5-1-6-3-7-'
    8 <- '4-2-5-1-6-3-7-'
    8 -- '4-2-5-1-6-3-7-8-'
    8 -> '4-2-5-1-6-3-7-8-'
   7 -> '4-2-5-1-6-3-7-8-'
  3 -> '4-2-5-1-6-3-7-8-'
 1 -> '4-2-5-1-6-3-7-8-'

缩进显示堆栈的深度。每个单独的递归调用都包含三个部分——左孩子、self.data 和右孩子。

代码“知道” 2 在 4 之后,因为 4 发生在 2 的调用开始时,并且字符串 '2-' 紧随其后:

  2 <- ''          # start of 2's call.  2 starts by calling 4
   4 <- ''             # start of 4's call
   4 -- '4-'           # 4 appends self.data
   4 -> '4-'           # end of 4's call, returning to 2
  2 -- '4-2-'      # 2 appends self.data to what it got from 4, and calls 5
   5 <- '4-2-'         # start of 5's call
   5 -- '4-2-5-'       # 5 appends self.data
   5 -> '4-2-5-'       # end of 5's call, returning to 2
  2 -> '4-2-5-'    # end of 2's call

【讨论】:

    【解决方案2】:

    好的,再坐一会儿,想通了。想法 id 发布我的想法,以便可以将此问题标记为已回答。

    1. inorder func 使用根和空字符串调用。
    2. start 的值为 tree.root,而 start 的值为它在 traversal = self.inorder_print(start.left, traversal) 处不断递归
    3. 需要注意的是,每次发生递归时,您都会深入到树中。 start.data = 1. recursion, start.data = 2 recursion, start.data = 4. 因为 4 没有左孩子。函数终于可以返回了。 start.data now = 4 并且下一行 traveral += str(start.data) + "-") 可以运行,添加到字符串中。 但是请记住,该函数仍然有 2 个递归深度。我们的 start.data 回到 2。当该函数完成执行时,通过 line traveral += str(start.data) + "-")/ 将 2 添加到遍历中 #recursion 发生在 start.right 上。 start.data now =5 start.right 没有值,所以函数可以返回 5。这个过程持续到整个树,直到所有已经执行的函数都完成。 理解的诀窍(至少对我而言)是,当您返回到更高级别的递归时,函数会从它离开的地方开始,而不是从头开始。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 2021-08-06
      相关资源
      最近更新 更多