二叉树入门必刷 4 题:掌握递归三板斧,搞定 80% 二叉树题目
前言
很多人刚开始刷二叉树的时候都有一个共同感受:
看懂了题解,但下次遇到类似题还是不会写。
原因很简单。
二叉树题目看起来很多,但本质上都在考察几种固定模式:
- 遍历树
- 计算树的属性
- 修改树的结构
- 判断树的关系
今天刷完 LeetCode 的 4 道经典二叉树题后,我发现它们刚好覆盖了二叉树最核心的知识体系:
-
- 二叉树中序遍历
-
- 二叉树最大深度
-
- 翻转二叉树
-
- 对称二叉树
如果能够真正理解这四道题,后面绝大多数树题都会轻松很多。
本文就从题目、技巧、模板和易错点四个角度进行系统总结。
为什么二叉树题目都喜欢用递归?
先看一棵简单二叉树:
1
/ \
2 3
/ \
4 5
会发现一个特点:
节点下面还是树。
左子树是一棵树。
右子树也是一棵树。
因此二叉树天然具有:
大问题 = 左子树问题 + 右子树问题
这种结构特别适合递归。
所以刷树时一定记住:
能用递归解决的树题,优先考虑递归。
LeetCode 94:二叉树中序遍历
题目要求
按照:
左子树 → 根节点 → 右子树
顺序遍历整棵树。
递归思路
中序遍历其实就是严格执行:
遍历左子树
访问当前节点
遍历右子树
代码框架:
def dfs(node):
if not node:
return
dfs(node.left)
res.append(node.val)
dfs(node.right)
本题考察什么?
实际上考察的是:
树的遍历思想
后续所有遍历题:
- 前序遍历
- 后序遍历
- 路径问题
- 节点统计
都来源于这一套框架。
易错点
忘记终止条件
if not node:
return
这是所有树递归的生命线。
遍历顺序写错
很多同学会把:
左 → 根 → 右
写成:
根 → 左 → 右
结果变成前序遍历。
LeetCode 104:二叉树最大深度
题目要求
求根节点到最远叶子节点的路径长度。
核心公式
这题建议直接背下来:
空节点深度 = 0
当前节点深度 =
max(左子树深度, 右子树深度) + 1
递归代码框架
def maxDepth(root):
if not root:
return 0
left = maxDepth(root.left)
right = maxDepth(root.right)
return max(left, right) + 1
本题考察什么?
考察:
分治思想
把整棵树拆成:
求左子树深度
求右子树深度
最后再合并答案。
易错点
忘记加 1
错误写法:
return max(left, right)
正确写法:
return max(left, right) + 1
因为当前节点本身也算一层。
LeetCode 226:翻转二叉树
题目要求
把整棵树左右镜像翻转。
例如:
原树:
1
/ \
2 3
翻转后:
1
/ \
3 2
核心思想
很多人第一反应:
交换 val
这是错的。
真正翻转的是:
left 指针
right 指针
而不是节点值。
递归框架
def invertTree(root):
if not root:
return None
root.left, root.right = (
invertTree(root.right),
invertTree(root.left)
)
return root
本题考察什么?
考察:
修改树结构
之前两题是读取树。
这题开始修改树。
易错点
交换节点值
错误:
root.left.val
root.right.val
交换数值。
正确:
root.left
root.right
交换指针。
LeetCode 101:对称二叉树
题目要求
判断一棵树是否关于中心轴对称。
例如:
1
/ \
2 2
/ \ / \
3 4 4 3
属于对称树。
核心思想
不要判断:
左子树是否对称
右子树是否对称
这是错误思路。
应该判断:
左子树和右子树是否互为镜像
镜像判断规则
给定两个节点:
p
q
需要满足:
情况1
p == None
q == None
返回 True
情况2
一个为空一个不为空
返回 False
情况3
值相等
同时满足:
p.left ↔ q.right
p.right ↔ q.left
递归框架
def check(p, q):
if not p and not q:
return True
if not p or not q:
return False
return (
p.val == q.val
and check(p.left, q.right)
and check(p.right, q.left)
)
本题考察什么?
这是第一道典型:
双节点递归
前面题目递归参数:
dfs(node)
只有一个节点。
这里变成:
check(node1, node2)
同时处理两个节点。
这是后面:
- 相同的树
- 子树判断
- 镜像树
等题目的基础。
二叉树递归三板斧
今天四道题全部遵循同一个套路。
以后看到树题都先套这个模板。
第一步:递归终止
if not node:
return ?
第二步:递归处理左右子树
left = dfs(node.left)
right = dfs(node.right)
第三步:处理当前节点
根据题目需求:
- 收集节点
- 计算深度
- 交换指针
- 判断镜像
记住一句话:
先解决左右子树,再处理当前节点。
这是树递归的核心思想。
什么时候需要辅助函数?
很多同学刷树时经常纠结:
要不要写内部函数?
其实规律很简单。
不需要辅助函数
只有一个节点参数。
结果通过 return 返回。
例如:
- 最大深度
- 翻转二叉树
需要辅助函数
收集结果
例如:
res.append(...)
需要共享列表。
多参数递归
例如:
check(p, q)
需要同时处理多个节点。
传递额外信息
例如:
- 当前路径
- 当前层数
- 路径和
都需要辅助函数。
高频易错点总结
1. 忘记空节点判断
所有树递归第一件事:
if not root:
2. 搞混结构和数值
翻转树:
交换指针。
不是交换 val。
3. 忘记递归返回值
例如最大深度:
return max(left,right)+1
必须返回。
4. 镜像比较顺序写错
正确:
left.left ↔ right.right
left.right ↔ right.left
5. 把节点数量当成树深度
深度:
层数
不是:
节点总数
总结
今天这四道题虽然看起来不同,但本质都在训练同一个能力:
递归处理二叉树。
如果把二叉树比作一个知识体系:
- 94 中序遍历 → 学会遍历
- 104 最大深度 → 学会计算属性
- 226 翻转二叉树 → 学会修改结构
- 101 对称二叉树 → 学会判断关系
真正掌握这四道题之后,后续大部分树题都能快速找到解题方向。
最后留一个思考题:
为什么二叉树最大深度不需要辅助函数,而中序遍历却必须借助辅助函数或额外列表?
欢迎在评论区交流你的理解。

13

被折叠的 条评论
为什么被折叠?



