Files
docs/docs/algorithm/binary-tree-traversal.md
T
wonder 126a26c063
Deploy Docs / deploy (push) Successful in 9s
add: 二叉树的遍历 笔记
2026-08-25 07:17:47 +00:00

7.5 KiB
Raw Blame History

二叉树的遍历

!!! note "💡 一句话概述" 二叉树遍历是按照特定规则走访每个节点恰好一次,核心分为深度优先(前/中/后序)和广度优先(层序)两大类。


🔑 核心概念

  1. 前序遍历(Pre-order):根 → 左 → 右,常用于序列化/复制树结构
  2. 中序遍历(In-order):左 → 根 → 右,BST 中序即有序序列
  3. 后序遍历(Post-order):左 → 右 → 根,常用于释放资源、计算目录大小
  4. 层序遍历(Level-order / BFS):逐层从左到右,借助队列实现

📝 详细说明

遍历方式对比

遍历方式 访问顺序 典型应用 实现方式
前序 根→左→右 序列化、复制树 递归 / 栈
中序 左→根→右 BST 排序输出 递归 / 栈
后序 左→右→根 释放节点、表达式求值 递归 / 栈
层序 逐层从左到右 最短路径、按层处理 队列

递归 vs 迭代

递归写法简洁直观,但存在栈溢出风险(树深度过大时);迭代写法用显式栈/队列模拟调用栈,更安全也更可控。面试中通常要求两种都能写。

统一迭代模板

三种 DFS 遍历的迭代写法可以用「标记法」统一:入栈时按相反顺序压入节点和 null 标记,遇到 null 时才真正访问节点,通过调整压入顺序即可切换前/中/后序。


💻 代码示例

节点定义

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

递归写法

def preorder(root):
    if not root:
        return []
    return [root.val] + preorder(root.left) + preorder(root.right)

def inorder(root):
    if not root:
        return []
    return inorder(root.left) + [root.val] + inorder(root.right)

def postorder(root):
    if not root:
        return []
    return postorder(root.left) + postorder(root.right) + [root.val]

迭代写法

def preorder_iter(root):
    """前序迭代:栈,先右后左压入"""
    if not root:
        return []
    stack, res = [root], []
    while stack:
        node = stack.pop()
        res.append(node.val)
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)
    return res

def inorder_iter(root):
    """中序迭代:一路向左入栈,弹出后转向右子树"""
    stack, res = [], []
    cur = root
    while cur or stack:
        while cur:
            stack.append(cur)
            cur = cur.left
        cur = stack.pop()
        res.append(cur.val)
        cur = cur.right
    return res

def postorder_iter(root):
    """后序迭代:前序(根左右)翻转 → 根右左 → 反转得左右根"""
    if not root:
        return []
    stack, res = [root], []
    while stack:
        node = stack.pop()
        res.append(node.val)
        if node.left:
            stack.append(node.left)
        if node.right:
            stack.append(node.right)
    return res[::-1]

层序遍历(BFS)

from collections import deque

def levelorder(root):
    if not root:
        return []
    q, res = deque([root]), []
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft()
            level.append(node.val)
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        res.append(level)
    return res

统一迭代模板(标记法)

def inorder_unified(root):
    """统一模板:切换压入顺序即可实现前/中/后序"""
    if not root:
        return []
    stack, res = [root], []
    while stack:
        node = stack.pop()
        if node is None:
            # null 标记,下一个就是真正要访问的节点
            val_node = stack.pop()
            res.append(val_node.val)
        else:
            # 中序:左 → 根 → 右,入栈顺序相反:右 → 根(含标记) → 左
            if node.right:
                stack.append(node.right)
            stack.append(node)      # 节点本身
            stack.append(None)      # null 标记
            if node.left:
                stack.append(node.left)
    return res

⚠️ 常见陷阱

!!! warning "中序迭代漏掉右子树" 在中序迭代中,弹出节点后必须 cur = node.right,而不是继续向左走。忘记转向右子树会导致死循环或遗漏节点。

!!! warning "后序迭代的反转技巧" 后序迭代用「前序翻转法」时,注意压栈顺序是先左后右(与标准前序相反),这样弹出顺序变成 根→右→左,反转后才是 左→右→根。

!!! warning "层序遍历中 queue 长度要在循环外快照" for _ in range(len(q)) 必须在 for 循环开始前锁住当前层的节点数。如果在循环内动态取 len(q),会把新入队的下一层节点也算进来。


🏋️ 练习题

??? question "练习 1:LeetCode 144 — 二叉树的前序遍历" 给你二叉树的根节点 root,返回它节点值的前序遍历。请用迭代方式实现。

??? success "答案"
    ```python
    def preorderTraversal(root):
        if not root:
            return []
        stack, res = [root], []
        while stack:
            node = stack.pop()
            res.append(node.val)
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return res
    ```

??? question "练习 2:LeetCode 94 — 二叉树的中序遍历" 给定一个二叉树的根节点 root,返回它的中序遍历。请用迭代方式实现。

??? success "答案"
    ```python
    def inorderTraversal(root):
        stack, res = [], []
        cur = root
        while cur or stack:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            res.append(cur.val)
            cur = cur.right
        return res
    ```

??? question "练习 3:LeetCode 102 — 二叉树的层序遍历" 给你二叉树的根节点 root,返回其节点值的层序遍历(即逐层地,从左到右访问所有节点)。

??? success "答案"
    ```python
    from collections import deque

    def levelOrder(root):
        if not root:
            return []
        q, res = deque([root]), []
        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            res.append(level)
        return res
    ```

🔗 相关链接