本篇题目
基础结构
class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right示例树(以下四种遍历都用这棵树演示):
1 / \ 2 3 / \ 4 5一、144. 前序遍历(Preorder)
思路
根 → 左 → 右。先记录当前节点,再递归处理左子树,最后递归处理右子树。
示例
访问顺序: 1. 记录根 1 2. 进入左子树,记录 2 3. 继续进入左子树,记录 4 4. 4 没有子节点,回退 5. 进入 2 的右子树,记录 5 6. 5 没有子节点,回退 7. 进入根的右子树,记录 3
结果:[1, 2, 4, 5, 3]代码
class Solution: def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: res = []
def helper(root): if not root: return res.append(root.val) # 根 helper(root.left) # 左 helper(root.right) # 右
helper(root) return resTIP前序遍历常用于复制一棵树或序列化树结构,因为先输出根节点,重建时能先确定父节点再确定子节点。
二、94. 中序遍历(Inorder)
思路
左 → 根 → 右。先递归处理左子树,再记录当前节点,最后递归处理右子树。
示例
访问顺序: 1. 一路往左走到 4 2. 4 没有左子树,记录 4 3. 回退,记录 2 4. 进入 2 的右子树,记录 5 5. 回退到根,记录 1 6. 进入右子树,记录 3
结果:[4, 2, 5, 1, 3]代码
class Solution: def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: res = []
def helper(root): if not root: return helper(root.left) # 左 res.append(root.val) # 根 helper(root.right) # 右
helper(root) return resTIP对二叉搜索树(BST)做中序遍历,结果一定是升序排列的。很多 BST 相关的题(验证 BST、BST 第 k 小的元素等)都会用到这个性质。
三、145. 后序遍历(Postorder)
思路
左 → 右 → 根。先递归处理左子树,再递归处理右子树,最后记录当前节点。
示例
访问顺序: 1. 一路往左走到 4 2. 4 没有子节点,记录 4 3. 回退到 2,进入右子树 4. 5 没有子节点,记录 5 5. 左右都处理完,记录 2 6. 回退到根,进入右子树 7. 3 没有子节点,记录 3 8. 左右都处理完,记录根 1
结果:[4, 5, 2, 3, 1]代码
class Solution: def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: res = []
def helper(root): if not root: return helper(root.left) # 左 helper(root.right) # 右 res.append(root.val) # 根
helper(root) return resTIP后序遍历常用于删除树或计算目录大小的场景——必须先处理完所有子节点,才能处理父节点。比如删除文件夹时,要先删里面的文件,再删文件夹本身。
四、102. 层序遍历(Level Order)
思路
从上到下,从左到右,一层一层地访问。用 BFS(队列)实现:把根放入队列,每次取出一个节点就把它的左右子节点入队,直到队列为空。
示例
第 1 层:[1]第 2 层:[2, 3]第 3 层:[4, 5]
结果:[[1], [2, 3], [4, 5]]代码
CAUTION空节点判断必须写在最前面
和递归遍历不同,BFS 需要在函数入口处先判断
if not root: return []。如果不判断直接deque([root]),会把None入队,后面取node.left/node.right就会报错。
from collections import deque
class Solution: def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: if not root: return []
res = [] # 先让根节点入队 queue = deque([root])
while queue: # 当队列不为空的时候 队列为空的时候代表我们把所有层遍历完了 因为在处理完某一层的时候 我们已经把下一层的左右节点(如果有的话)提前放进队列中了 所以len(queue)是有值的 # 每次遍历要创建当前层的列表 level = [] for i in range(len(queue)): # 每次只处理当前层的节点 这一层有多少个节点for循环就执行多少次 # 从队列左边弹出 要先弹出才能获取node的val 我们才能将这个节点加入到这层的level列表里面 node = queue.popleft() # 随后加入到此层的列表里面 level.append(node.val) # 如果这个节点的左右还有孩子的话就加入队列 if node.left: queue.append(node.left) if node.right: queue.append(node.right) # 当for循环退出的时候 代表这一层已经处理完了 所以这时将当前层的level列表加入到总的大res列表中 [[],[],[]] res.append(level)
return resTIP入队前先判断
if node.left和if node.right,确保队列里只放有效节点,避免把大量None堆进队列浪费空间。这和递归遍历的习惯不同——递归是进去再判断,BFS 是入队前就过滤。
五、104. 二叉树最大深度(Maximum Depth)
思路
一个节点的最大深度 = 左右子树深度的较大值 + 1。用递归实现:空节点深度为 0,每向上返回一层就加 1。
示例
1 ← 深度 3 / \ 2 3 ← 深度 2 / \ 4 5 ← 深度 1
maxDepth(4) = 1maxDepth(5) = 1maxDepth(2) = 1 + max(1, 1) = 2maxDepth(3) = 1maxDepth(1) = 1 + max(2, 1) = 3代码
class Solution: def maxDepth(self, root: Optional[TreeNode]) -> int: if not root: return 0 # 空节点深度为 0
left = self.maxDepth(root.left) # 左子树深度 right = self.maxDepth(root.right) # 右子树深度
return 1 + max(left, right) # 当前节点深度 = 较深的子树 + 1TIP注意空节点要返回
0(整数),不能返回[]。因为后面要做1 + max(left, right),max接收的必须是数字,传入列表会报错。返回值的类型要和题目要求一致。
总结对比
1 / \ 2 3 / \ 4 5
前序(根左右):[1, 2, 4, 5, 3]中序(左根右):[4, 2, 5, 1, 3]后序(左右根):[4, 5, 2, 3, 1]层序(逐层): [[1], [2, 3], [4, 5]]最大深度: 3| 题目 | 思路 | 实现 | 典型用途 |
|---|---|---|---|
| 前序遍历 | 根→左→右 | 递归 | 复制树、序列化 |
| 中序遍历 | 左→根→右 | 递归 | BST 升序输出 |
| 后序遍历 | 左→右→根 | 递归 | 删除树、计算大小 |
| 层序遍历 | 逐层从左到右 | BFS 队列 | 求树的高度、锯齿遍历 |
| 最大深度 | 左右子树深度较大值 + 1 | 递归 | 判断平衡树、路径问题 |
记忆口诀:前中后指的是根的位置,左永远在右前面。