1. 项目背景解析
最近在整理算法题库时,发现OJ平台上的104、105、106这三道题目形成了一个非常有意思的递进关系。这三道题都围绕着二叉树展开,但考察的侧重点各不相同,正好构成了一个从基础到进阶的学习路径。作为刷过300+算法题的过来人,我想分享一下这三道题的解题思路和它们之间的内在联系。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 题目分析与解题思路
2.1 OJ104:二叉树的最大深度
这是最基础的二叉树遍历问题,要求计算二叉树的最大深度。我推荐使用递归解法,代码简洁易懂:
python复制def maxDepth(root):
if not root:
return 0
return max(maxDepth(root.left), maxDepth(root.right)) + 1
注意:虽然递归解法简单,但要注意栈溢出风险。对于极端不平衡的树(比如退化成链表),建议使用BFS层序遍历。
2.2 OJ105:从前序与中序遍历序列构造二叉树
这道题考察对二叉树遍历性质的理解。解题关键在于:
- 前序数组的第一个元素是根节点
- 在中序数组中找到根节点位置,左边是左子树,右边是右子树
python复制def buildTree(preorder, inorder):
if not preorder or not inorder:
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
2.3 OJ106:从中序与后序遍历序列构造二叉树
这道题是105的变种,解题思路类似但需要注意:
- 后序数组的最后一个元素是根节点
- 同样需要在中序数组中找到分割点
python复制def buildTree(inorder, postorder):
if not inorder or not postorder:
return None
root_val = postorder[-1]
root = TreeNode(root_val)
idx = inorder.index(root_val)
root.left = buildTree(inorder[:idx], postorder[:idx])
root.right = buildTree(inorder[idx+1:], postorder[idx:-1])
return root
3. 核心算法解析
3.1 递归与分治思想
这三道题都体现了递归和分治的思想:
- 将大问题分解为小问题
- 解决小问题
- 合并小问题的解
在二叉树问题中,这种思想表现为:
- 处理当前节点
- 递归处理左子树
- 递归处理右子树
3.2 时间复杂度分析
对于构造二叉树的问题(105和106):
- 最坏时间复杂度:O(n^2)(当树极度不平衡时)
- 平均时间复杂度:O(nlogn)
- 可以通过哈希表优化到O(n)
4. 优化与进阶
4.1 使用哈希表优化
为了快速定位中序数组中的根节点位置,可以预先建立值到索引的映射:
python复制def buildTree(preorder, inorder):
inorder_map = {val:idx for idx, val in enumerate(inorder)}
def helper(pre_start, pre_end, in_start, in_end):
if pre_start > pre_end:
return None
root_val = preorder[pre_start]
root = TreeNode(root_val)
idx = inorder_map[root_val]
left_size = idx - in_start
root.left = helper(pre_start+1, pre_start+left_size, in_start, idx-1)
root.right = helper(pre_start+left_size+1, pre_end, idx+1, in_end)
return root
return helper(0, len(preorder)-1, 0, len(inorder)-1)
4.2 迭代解法
对于最大深度问题,可以使用层序遍历的迭代解法:
python复制def maxDepth(root):
if not root:
return 0
queue = [root]
depth = 0
while queue:
depth += 1
level_size = len(queue)
for _ in range(level_size):
node = queue.pop(0)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
5. 常见错误与调试技巧
5.1 数组切片错误
在105和106题中,最常见的错误是数组切片范围不正确。建议:
- 画图理解索引关系
- 打印每次递归的输入参数
- 使用小例子手动验证
5.2 空指针处理
总是要检查输入是否为空:
python复制if not preorder or not inorder:
return None
5.3 测试用例设计
建议测试这些边界情况:
- 空树
- 只有根节点的树
- 左斜树/右斜树
- 完全二叉树
6. 题目之间的联系
这三道题形成了一个完整的学习闭环:
- 104教你理解二叉树的结构
- 105教你如何从前序+中序重建二叉树
- 106教你如何从中序+后序重建二叉树
理解这三道题后,你可以尝试解决:
- 根据前序和后序重建二叉树(需要额外条件)
- 序列化和反序列化二叉树
- 验证二叉搜索树的前序/后序遍历序列
7. 个人心得
在实际刷题过程中,我发现:
- 画图是理解二叉树问题的最佳方式
- 递归解法虽然简洁,但要注意栈深度限制
- 迭代解法通常更高效,但代码更复杂
- 测试用例要覆盖各种边界情况
- 时间复杂度分析不能忽视最坏情况
对于想深入理解二叉树的同学,我建议按照这个顺序练习:
- 遍历问题(前中后序)
- 深度/宽度问题
- 构建问题
- 序列化问题
- 特殊二叉树(BST、AVL等)
