繁体   English   中英

使用递归解释的二叉树遍历(python)

[英]Binary Tree Traversal Using Recursion Explanation (python)

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

编辑为了增加问题的清晰度:

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

我的困惑是,我似乎无法找到代码在哪里,“知道”它遍历到 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, ""))

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

    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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM