1. 刷题计划背景与价值
最近在整理自己的算法能力体系时,发现树形数据结构一直是薄弱环节。恰好力扣(LeetCode)的每日一题和专题训练中,树相关题目出现的频率极高。于是决定在2月26日至3月5日这8天时间里,集中攻克树形数据结构的经典题型。这个时间段的选题主要覆盖了二叉树、二叉搜索树、N叉树的基础操作和进阶应用。
为什么要专门做树形结构的专项训练?在实际工程中,从文件系统目录树到DOM树渲染,从数据库索引B+Tree到推荐系统的决策树,树结构无处不在。面试中更是高频考点,像最近帮团队做技术面试时,90%的候选人都在二叉树遍历这类基础题上暴露出思维漏洞。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目筛选与分类策略
2.1 基础题型筑基
从力扣题库中筛选出32道经典题目,按难度和知识点分为四个梯度:
-
遍历与构造(8题)
- 前中后序遍历(递归/迭代)
- 层序遍历及其变种
- 根据遍历序列重构二叉树
-
属性与判断(10题)
- 对称性判断(#101)
- 深度/高度计算(#104)
- 平衡二叉树验证(#110)
- 路径总和系列(#112/#113/#437)
-
特殊结构处理(7题)
- 二叉搜索树验证(#98)
- 最近公共祖先(#235/#236)
- 序列化与反序列化(#297)
-
综合应用题(7题)
- 二叉树转链表(#114)
- 删除二叉搜索树节点(#450)
- 打家劫舍III(#337)
2.2 每日训练计划
采用"3+1+1"的每日训练模式:
- 3道新题(不同知识点)
- 1道昨日错题复盘
- 1道随机旧题强化
例如Day1的训练组合:
- #94 中序遍历(基础)
- #102 层序遍历(基础)
- #105 前序+中序构造(进阶)
- 复盘昨日链表题#25
- 随机抽题#226 翻转二叉树
3. 核心解题技巧实录
3.1 遍历的六种实现范式
以#144前序遍历为例,演示不同实现方式的取舍:
python复制# 递归模板(最易理解)
def preorder(root):
if not root: return []
return [root.val] + preorder(root.left) + preorder(root.right)
# 迭代模板(显式栈)
def preorder(root):
stack, res = [root], []
while stack:
node = stack.pop()
if node:
res.append(node.val)
stack.append(node.right) # 右先入栈
stack.append(node.left)
return res
# Morris遍历(空间O(1))
def preorder(root):
res = []
while root:
if not root.left:
res.append(root.val)
root = root.right
else:
pred = root.left
while pred.right and pred.right != root:
pred = pred.right
if not pred.right:
res.append(root.val)
pred.right = root
root = root.left
else:
pred.right = None
root = root.right
return res
关键经验:面试时优先写递归版,被追问优化时再展示迭代版。Morris遍历仅在明确要求O(1)空间时使用。
3.2 构造问题的分治策略
以#105从前序与中序构造二叉树为例,分解步骤:
- 前序首元素为根节点值
- 在中序中找到该值位置index
- 左子树:
- 前序范围:[1, index+1)
- 中序范围:[0, index)
- 右子树:
- 前序范围:[index+1, end)
- 中序范围:[index+1, end)
python复制def buildTree(preorder, inorder):
if not preorder: return None
root_val = preorder[0]
root = TreeNode(root_val)
idx = inorder.index(root_val)
root.left = buildTree(preorder[1:idx+1], inorder[:idx])
root.right = buildTree(preorder[idx+1:], inorder[idx+1:])
return root
避坑指南:Python的切片操作时间复杂度为O(n),可改为传递索引范围+字典缓存优化:
python复制def buildTree(preorder, inorder):
idx_map = {val:idx for idx,val in enumerate(inorder)}
def helper(pre_left, pre_right, in_left, in_right):
if pre_left > pre_right: return None
root_val = preorder[pre_left]
root = TreeNode(root_val)
idx = idx_map[root_val]
left_size = idx - in_left
root.left = helper(pre_left+1, pre_left+left_size, in_left, idx-1)
root.right = helper(pre_left+left_size+1, pre_right, idx+1, in_right)
return root
return helper(0, len(preorder)-1, 0, len(inorder)-1)
4. 高频考点深度剖析
4.1 最近公共祖先(LCA)问题
以#236二叉树的最近公共祖先为例,对比两种解法:
解法一:后序遍历回溯法(最优)
python复制def lowestCommonAncestor(root, p, q):
if not root or root == p or root == q:
return root
left = lowestCommonAncestor(root.left, p, q)
right = lowestCommonAncestor(root.right, p, q)
if left and right: # 当前节点是LCA
return root
return left or right # 返回非空子树的结果
解法二:父指针哈希法
- 用DFS遍历记录每个节点的父指针
- 从p节点向上回溯并记录访问路径
- 从q节点向上查找第一个重合的节点
对比结论:方法一更优雅但需要理解递归栈,方法二更直观适合不熟悉递归的开发者。二叉搜索树的LCA(#235)可以利用BST性质进一步优化。
4.2 路径总和问题
#112基础版与#113进阶版的解题差异:
python复制# 112简单版(存在性判断)
def hasPathSum(root, target):
if not root: return False
if not root.left and not root.right:
return root.val == target
return hasPathSum(root.left, target-root.val) or hasPathSum(root.right, target-root.val)
# 113进阶版(路径记录)
def pathSum(root, target):
res = []
def dfs(node, path, remain):
if not node: return
path.append(node.val)
if not node.left and not node.right and remain == node.val:
res.append(list(path))
dfs(node.left, path, remain-node.val)
dfs(node.right, path, remain-node.val)
path.pop() # 关键回溯步骤
dfs(root, [], target)
return res
易错点:进阶版必须注意Python列表的可变性,每次找到有效路径时需要
list(path)创建副本,否则后续的pop()操作会影响已存储的结果。
5. 刷题成效与经验总结
5.1 训练成果统计
8天共完成32道题目,各类型通过率:
- 基础遍历:100%(8/8)
- 属性判断:90%(9/10)
- 结构处理:85%(6/7)
- 综合应用:71%(5/7)
典型错误集中在:
- 迭代遍历时栈的出入顺序混淆(特别是中序遍历)
- 构造问题时忽略输入序列的边界条件
- 路径类问题忘记状态回溯
5.2 效率提升技巧
- 模板化编码:将遍历代码整理成可复用的代码片段。例如层序遍历模板:
python复制def levelOrder(root):
if not root: return []
queue = collections.deque([root])
res = []
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
res.append(level)
return res
- 可视化调试:对于复杂树问题,先手动绘制示例树的形状,标注指针移动路径。例如处理#297序列化问题时,画出如下结构帮助理解:
code复制 1
/ \
2 3
/ \
4 5
序列化结果:"1,2,null,null,3,4,null,null,5,null,null"
- 复杂度分析表:
| 题号 | 解法 | 时间复杂度 | 空间复杂度 |
|------|----------------|------------|------------|
| #101 | 递归比较 | O(n) | O(h) |
| #104 | DFS递归 | O(n) | O(h) |
| #110 | 后序遍历优化 | O(n) | O(h) |
| #114 | 前序迭代 | O(n) | O(1) |
5.3 后续提升方向
-
扩展题型:
- 红黑树等高级树结构
- 树形DP问题(如#337打家劫舍III)
- 字典树(Trie)应用
-
工程实践:
- 将刷题经验应用到实际场景,如:
- 文件系统遍历优化
- 组织架构树查询加速
- 决策树算法实现
-
持续训练计划:
- 每周保持3-5道树形题练习
- 每月一次专题复盘
- 建立错题本记录非常规解法
