二叉树高频考点:由后序+中序遍历推导前序与层序遍历(附完整Python代码)
> 摘要:二叉树遍历是算法面试的必考题型。本文从原理到代码,完整拆解"后序+中序→前序/层序"的推导过程,包含详细图解、可运行代码及4大避坑指南,建议收藏备用。
一、四种遍历方式速览
二叉树遍历的本质是根节点、左子树、右子树访问顺序的不同:
| 遍历方式 | 访问顺序 | 类型 | 作用 |
|---|---|---|---|
| 前序遍历 | 根 → 左 → 右 | DFS | 目标输出 |
| 中序遍历 | 左 → 根 → 右 | DFS | 🔑 分割左右子树 |
| 后序遍历 | 左 → 右 → 根 | DFS | 🔑 确定根节点 |
| 层序遍历 | 从上到下、从左到右 | BFS | 目标输出 |
> 核心结论 :只要知道后序 + 中序,就能唯一确定一棵二叉树,进而推导任意遍历结果。
二、重建二叉树的核心逻辑
2.1 算法步骤(三步走)
① 找根节点:后序遍历的最后一个元素 = 当前子树的根
② 分割子树:在中序遍历中找到根的位置,左侧=左子树,右侧=右子树
③ 递归重建:对左右子树重复步骤①②
2.2 关键细节:必须先右后左!
⚠️ 避坑重点 :递归重建时,必须先重建右子树,再重建左子树。
原因 :我们使用 post.pop() 从后序数组末尾取元素,先取出的是右子树的根节点。若先建左子树,会导致节点匹配错误,重建失败。
三、完整Python实现
python
from collections import deque
class TreeNode:
"""二叉树节点定义"""
def __init__(self, val=0):
self.val = val
self.left = None
self.right = None
def build_tree(inorder, postorder):
"""
根据中序和后序遍历重建二叉树
:param inorder: 中序遍历列表 [左→根→右]
:param postorder: 后序遍历列表 [左→右→根]
:return: 二叉树根节点
"""
if not inorder:
return None
# 步骤1:后序末尾元素 = 当前根节点
root_val = postorder.pop()
root = TreeNode(root_val)
# 步骤2:在中序中定位根节点,分割左右子树
mid_idx = inorder.index(root_val)
# 步骤3:⚠️ 必须先建右子树,再建左子树(适配pop()顺序)
root.right = build_tree(inorder[mid_idx + 1:], postorder)
root.left = build_tree(inorder[:mid_idx], postorder)
return root
def preorder_traversal(root):
"""前序遍历:根 → 左 → 右(递归版)"""
if not root:
return []
return [root.val] + preorder_traversal(root.left) + preorder_traversal(root.right)
def level_order_traversal(root):
"""层序遍历:BFS队列实现,按层分组返回"""
if not root:
return []
q = deque([root])
res = []
while q:
level_size = len(q) # 🔑 记录当前层节点数
current_level = []
for _ in range(level_size):
node = q.popleft()
current_level.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
res.append(current_level)
return res
# ==================== 测试用例 ====================
if __name__ == "__main__":
inorder = [9, 3, 15, 20, 7]
postorder = [9, 15, 7, 20, 3]
# 1. 重建二叉树
root = build_tree(inorder, postorder)
# 2. 推导前序遍历
preorder = preorder_traversal(root)
# 3. 推导层序遍历(两种格式)
level_order = level_order_traversal(root)
level_flat = [val for level in level_order for val in level]
print(f"中序遍历:{inorder}")
print(f"后序遍历:{postorder}")
print(f"前序遍历:{preorder}")
print(f"层序遍历(按层):{level_order}")
print(f"层序遍历(扁平):{level_flat}")
四、运行结果与图解
4.1 输出结果
中序遍历:[9, 3, 15, 20, 7]
后序遍历:[9, 15, 7, 20, 3]
前序遍历:[3, 9, 20, 15, 7]
层序遍历(按层):[[3], [9, 20], [15, 7]]
层序遍历(扁平):[3, 9, 20, 15, 7]
4.2 重建过程图解
以测试用例为例,重建步骤如下:
后序:[9, 15, 7, 20, 3] → 根=3
中序:[9, 3, 15, 20, 7] → 左=[9], 右=[15,20,7]
3
/ \
9 [15,20,7]
后序弹出20 → 右子树根=20
中序[15,20,7] → 左=[15], 右=[7]
3
/ \
9 20
/ \
15 7
4.3 遍历路径可视化
前序遍历路径(根→左→右):3 → 9 → 20 → 15 → 7
层序遍历路径(逐层访问):
第1层:[3]
第2层:[9, 20]
第3层:[15, 7]
五、四大避坑指南
| 坑点 | 错误做法 | 正确做法 |
|---|---|---|
| 1. 递归顺序 | 先左后右 | ⚠️ 必须先右后左(适配pop()) |
| 2. 重复节点 | 直接用index() |
用字典预存中序索引,避免值重复干扰 |
| 3. 层序层级混乱 | 不记录当前层节点数 | 用level_size控制每层遍历次数 |
| 4. 空树边界 | 忘记处理空列表 | 入口处判断if not inorder: return None |
核心口诀
后序找根,中序分边,先右后左,重建完成