zoukankan      html  css  js  c++  java
  • 二叉树的遍历详解:前、中、后、层次遍历(Python实现)

    二叉树的遍历详解:前、中、后、层次遍历(Python实现)

    二叉树是一种常见的数据结构,而它的常见遍历方法有前序遍历、中序遍历、后续遍历、层次遍历——掌握这几种遍历方法是很有必要的。
    假设我们二叉树节点的定义如下——

    # Definition for a binary tree node.
    class TreeNode:
        def __init__(self, x):
            self.val = x
            self.left = None
            self.right = None
    

    前序遍历

    对应的 Leetcode 习题

    1. 深度优先遍历(递归实现)

    我们先看前序遍历二叉树的过程——

    1. 访问根结点
    2. 前序遍历左子树
    3. 前序遍历右子树

    很容易就可以看出这个过程是递归的,所以可以很方便使用递归实现前序遍历。

    def preorderTraversal(root: TreeNode) -> List[int]:
        res = []
        dfs(root, res)
        return res
            
    def dfs(root: TreeNode, res: List[int]) -> None:
        if not root: return
        res.append(root.val)
        dfs(root.left, res)
        dfs(root.right, res)
    

    python 特色的二叉树前序遍历递归实现

    def preorderTraversal(root: TreeNode) -> List[int]:
        if not root: return []
        return [root.val] + preorderTraversal(root.left) + preorderTraversal(root.right)
    

    2.深度优先遍历(迭代实现)

    由于递归是隐式的使用了栈(函数栈),所以也可以直接使用栈来实现递归。

    def preorderTraversal(root: TreeNode) -> List[int]:
        if root is None: return []
        
        res, stack = [], [root]
        while stack:
            node = stack.pop()
            if not node: continue
            res.append(node.val)
            
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
                
        return res
    

    3.(颜色)标记法[1]

    核心思想如下:

    • 使用标记记录节点的状态,比如使用 flag,当 flag = True 表示该节点未被访问
    • 如果遇到未被访问的节点,则令 flag = False,依次将右节点、左节点、自身入栈
    • 如果遇到已访问过的节点,则将节点的值输出

    而只需要调整左右节点的入栈顺序,就可以变为中序遍历和后序遍历了。

    def preorderTraversal(root: TreeNode) -> List[int]:
        res, stack = [], [(root, True)]
        while stack:
            node, flag = stack.pop()
            if not node: continue
            
            if flag:
                stack.append((node.right, True))
                stack.append((node.left, True))
                stack.append((node, False))
            else:
                res.append(node.val)
        
        return res
    

    中序遍历

    对应的 Leetcode 习题

    1.深度优先遍历(递归实现)

    中序遍历二叉树的过程——

    1. 中序遍历左子树
    2. 访问根结点
    3. 中序遍历右子树

    因此,很容易使用递归实现中序遍历。

    def inorderTraversal(root: TreeNode) -> List[int]:
        res = []
        dfs(root, res)
        return res
            
    def dfs(root: TreeNode, res: List[int]) -> None:
        if not root: return
        dfs(root.left, res)
        res.append(root.val)
        dfs(root.right, res)
    

    python 特色的二叉树中序遍历递归实现

    def inorderTraversal(root: TreeNode) -> List[int]:
        if not root: return []
        return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right)
    

    2.深度优先遍历(迭代实现)

    由于递归是隐式的使用了栈(函数栈),所以也可以直接使用栈来实现递归。

    def inorderTraversal(root: TreeNode) -> List[int]:
        res, stack, cur = [], [], root
        while stack or cur:
            while cur:
                stack.append(cur)
                cur = cur.left
            
            cur = stack.pop()
            res.append(cur.val)
            cur = cur.right
        return res
    

    3.(颜色)标记法[1:1]

    核心思想如下:

    • 使用标记记录节点的状态,比如使用 flag,当 flag = True 表示该节点未被访问
    • 如果遇到未被访问的节点,则令 flag = False,依次将右节点、自身、左节点入栈
    • 如果遇到已访问过的节点,则将节点的值输出

    与前序遍历相比,仅改变了左节点和当前节点的入栈顺序。

    def inorderTraversal(root: TreeNode) -> List[int]:
        res, stack = [], [(root, True)]
        while stack:
            node, flag = stack.pop()
            if not node: continue
            
            if flag:
                stack.append((node.right, True))
                stack.append((node, False))
                stack.append((node.left, True))
            else:
                res.append(node.val)
        
        return res
    

    后序遍历

    对应的 Leetcode 习题

    1.深度优先遍历(递归实现)

    后序遍历二叉树的过程——

    1. 后序遍历左子树
    2. 后序遍历右子树
    3. 访问根结点

    因此,很容易使用递归实现后序遍历。

    def postorderTraversal(root: TreeNode) -> List[int]:
        res = []
        dfs(root, res)
        return res
            
    def dfs(root: TreeNode, res: List[int]) -> None:
        if not root: return
        dfs(root.left, res)
        dfs(root.right, res)
        res.append(root.val)
    

    python 特色的二叉树后序遍历递归实现

    def postorderTraversal(root: TreeNode) -> List[int]:
        if not root: return []
        return postorderTraversal(root.left) + postorderTraversal(root.right) + [root.val]
    

    2.深度优先遍历(迭代实现)

    def postorderTraversal(root: TreeNode) -> List[int]:
        if root is None: return []
        
        res, stack = [], [root]
        while stack:
            node = stack.pop()
            if not node: continue
            res.append(node.val)
            
            if node.left:
                stack.append(node.left)
            if node.right:
                stack.append(node.right)
                
        return res[::-1]
    

    3.(颜色)标记法[1:2]

    核心思想如下:

    • 使用标记记录节点的状态,比如使用 flag,当 flag = True 表示该节点未被访问
    • 如果遇到未被访问的节点,则令 flag = False,依次将自身、右节点、左节点入栈
    • 如果遇到已访问过的节点,则将节点的值输出
    def postorderTraversal(root: TreeNode) -> List[int]:
        res, stack = [], [(root, True)]
        while stack:
            node, flag = stack.pop()
            if not node: continue
            
            if flag:
                stack.append((node, False))
                stack.append((node.right, True))
                stack.append((node.left, True))
            else:
                res.append(node.val)
        
        return res
    

    层次遍历

    对应的 Leetcode 习题

    1.深度优先遍历(递归实现)

    层次遍历相对于前中后序遍历而言,加多节点所在二叉树层数的信息。所以只需要添加一个变量 level 记录层数即可。

    def levelOrder(root: TreeNode) -> List[List[int]]:
        res = []
        dfs(root, 0)
        return res
        
    def dfs(root: TreeNode, level: int) -> None:
        if not root: return
        if len(res) <= level:
            res.append([])
        res[level].append(root.val)
        dfs(root.left, level+1)
        dfs(root.right, level+1)
    

    2.深度优先遍历(迭代实现)

    由于递归是隐式的使用了栈(函数栈),所以也可以直接使用栈来实现递归。
    我们不妨使用一个二元组 (node, level) 来表示状态。

    def levelOrder(root: TreeNode) -> List[int]:
        res, stack = [], [(root, 0)]
        while stack:
            node, level = stack.pop()
            if not node: continue
            if len(res) <= level:
                res.append([])
            res[level].append(node.val)
            
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
                
        return res
    

    3.(颜色)标记法[1:3]

    相对于前序遍历的标记法,层次遍历的标记法也仅仅添加了节点层数(level)的变量。

    def levelOrder(root: TreeNode) -> List[int]:
        res, stack = [], [(root, True, 0)]
        while stack:
            node, flag, level = stack.pop()
            if not node: continue
            
            if flag:
                stack.append((node.right, True, level+1))
                stack.append((node.left, True, level+1))
                stack.append((node, False))
            else:
                if len(res) <= level:
                    res.append([])
                res[level].append(node.val)
        
        return res
    

    4.广度优先遍历(队列实现)

    因为层次遍历是逐层遍历的,所以可以使用广度优先遍历。

    使用队列实现广度优先遍历,具体实现方法如下——

    1. 首先根节点入队
    2. 当队列不为空的时候
      • 当前队列长度 si (当前队列长度即为当前层的节点数)
      • 依次从队列中取出 si 元素,将其左右节点入队,进行下一次迭代
    def levelOrder(root: TreeNode) -> List[int]:
        if not root: return []
        
        res, queue = [], [root]
        while queue:
            level_node = []
            
            for _ in range(len(queue)):
                node = queue.pop(0)
                level_node.append(node.val)
                
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
                
            res.append(level_node)
            
        return res
    

    1. https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/ ↩︎ ↩︎ ↩︎ ↩︎

  • 相关阅读:
    CodeForcesGym 100524A Astronomy Problem
    ZOJ 2567 Trade
    HDU 3157 Crazy Circuits
    CodeForcesGym 100212E Long Dominoes
    UVALive 6507 Passwords
    [转]
    java socket
    Spark RDD Operations(2)
    Spark cache 和 persist
    vim 基础命令
  • 原文地址:https://www.cnblogs.com/justlikecode/p/13951782.html
Copyright © 2011-2022 走看看