二叉树的遍历详解:前、中、后、层次遍历(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/ ↩︎ ↩︎ ↩︎ ↩︎

posted @ 2020-11-10 00:07  康诚嘉士  阅读(996)  评论(0编辑  收藏  举报