二叉树的遍历¶
💡 一句话概述
二叉树遍历是按照特定规则走访每个节点恰好一次,核心分为深度优先(前/中/后序)和广度优先(层序)两大类。
🔑 核心概念¶
- 前序遍历(Pre-order):根 → 左 → 右,常用于序列化/复制树结构
- 中序遍历(In-order):左 → 根 → 右,BST 中序即有序序列
- 后序遍历(Post-order):左 → 右 → 根,常用于释放资源、计算目录大小
- 层序遍历(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
⚠️ 常见陷阱¶
中序迭代漏掉右子树
在中序迭代中,弹出节点后必须 cur = node.right,而不是继续向左走。忘记转向右子树会导致死循环或遗漏节点。
后序迭代的反转技巧
后序迭代用「前序翻转法」时,注意压栈顺序是**先左后右**(与标准前序相反),这样弹出顺序变成 根→右→左,反转后才是 左→右→根。
层序遍历中 queue 长度要在循环外快照
for _ in range(len(q)) 必须在 for 循环开始前锁住当前层的节点数。如果在循环内动态取 len(q),会把新入队的下一层节点也算进来。
🏋️ 练习题¶
练习 1:LeetCode 144 — 二叉树的前序遍历
给你二叉树的根节点 root,返回它节点值的**前序遍历**。请用迭代方式实现。
练习 2:LeetCode 94 — 二叉树的中序遍历
给定一个二叉树的根节点 root,返回它的**中序遍历**。请用迭代方式实现。
练习 3:LeetCode 102 — 二叉树的层序遍历
给你二叉树的根节点 root,返回其节点值的**层序遍历**(即逐层地,从左到右访问所有节点)。
🔗 相关链接¶
- LeetCode 144. 二叉树的前序遍历 — 前序迭代练习
- LeetCode 94. 二叉树的中序遍历 — 中序迭代练习
- LeetCode 145. 二叉树的后序遍历 — 后序迭代练习
- LeetCode 102. 二叉树的层序遍历 — 层序 BFS 练习