1. 二叉树专题:从Hot100真题看核心解题套路
二叉树作为数据结构中的常青树,在各类算法面试中出现的频率居高不下。最近在整理LeetCode Hot100高频题库时,我发现二叉树相关题目占比超过15%,其中既包含基础的遍历问题,也涉及复杂的递归应用。本文将结合这些真题,拆解二叉树问题的六大解题模板,并分享我在刷题过程中总结的实战技巧。
注:本文代码示例均以Python实现,但解题思路适用于所有编程语言。建议读者先自行尝试题目,再对照解析查漏补缺。
1.1 Hot100中的二叉树题目分布
在最新的Hot100榜单中,二叉树相关题目主要集中在以下类型:
- 基础遍历(前序/中序/后序/层序)
- 路径和问题(含最大路径和、路径总和等变种)
- 子树与对称性问题
- 构造二叉树(根据遍历序列重建)
- 二叉搜索树(BST)特性应用
- 最近公共祖先(LCA)问题
其中难度分布呈金字塔结构:简单题约占40%,中等难度50%,困难题10%。这提示我们在准备面试时,应该优先掌握中等及以下难度的核心解法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 二叉树遍历的四种武器
2.1 递归遍历:教科书式的标准实现
递归实现是理解二叉树遍历的基础框架,以中序遍历为例:
python复制def inorder(root):
if not root:
return []
return inorder(root.left) + [root.val] + inorder(root.right)
这种写法虽然简洁,但在面试中需要注意两个问题:
- 时间复杂度:每次拼接列表会产生O(n)开销,整体复杂度升至O(n²)
- 空间复杂度:递归调用栈深度可能达到O(n)
优化方案是使用辅助函数收集结果:
python复制def inorderTraversal(root):
res = []
def helper(node):
if not node: return
helper(node.left)
res.append(node.val)
helper(node.right)
helper(root)
return res
2.2 迭代遍历:面试官的进阶考察
迭代实现更能体现对遍历本质的理解。以前序遍历为例:
python复制def preorder(root):
res = []
stack = [root]
while stack:
node = stack.pop()
if node:
res.append(node.val)
stack.append(node.right) # 先右后左
stack.append(node.left)
return res
这里的关键点是:
- 使用栈模拟递归调用
- 入栈顺序与遍历顺序相反(前序是根左右,所以入栈顺序是根→右→左)
- 需要处理空节点情况
2.3 Morris遍历:O(1)空间的魔法
Morris遍历通过修改树结构实现O(1)空间复杂度,适合内存受限的场景。以下是中序Morris遍历的实现:
python复制def morris(root):
res = []
curr = root
while curr:
if not curr.left:
res.append(curr.val)
curr = curr.right
else:
# 找到前驱节点
pre = curr.left
while pre.right and pre.right != curr:
pre = pre.right
if not pre.right:
pre.right = curr # 建立线索
curr = curr.left
else:
pre.right = None # 拆除线索
res.append(curr.val)
curr = curr.right
return res
这种算法的核心在于利用空闲的right指针指向后继节点(线索二叉树思想)。虽然节省了空间,但会修改原树结构,且常数时间开销较大。
2.4 层序遍历:BFS的典型应用
层序遍历(LeetCode 102)是广度优先搜索的经典案例:
python复制from collections import deque
def levelOrder(root):
if not root: return []
res = []
q = deque([root])
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left: q.append(node.left)
if node.right: q.append(node.right)
res.append(level)
return res
变种题型包括:
- 锯齿形遍历(LeetCode 103)
- 右视图(LeetCode 199)
- 每层最大值(LeetCode 515)
3. 高频题型解题模板
3.1 路径和问题三板斧
路径和问题的典型代表是LeetCode 112(路径总和)和124(最大路径和)。其解题框架通常包含:
- 定义递归状态:当前节点、剩余/累计和
- 终止条件:到达叶子节点或路径不满足条件
- 状态转移:向左右子树传递更新后的状态
以路径总和II(LeetCode 113)为例:
python复制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中列表的引用特性,需要复制结果
3.2 子树问题双递归模式
判断子树(LeetCode 572)这类问题通常需要双重递归:
python复制def isSubtree(root, subRoot):
def isSame(s, t):
if not s and not t: return True
if not s or not t: return False
return s.val == t.val and isSame(s.left, t.left) and isSame(s.right, t.right)
if not root: return False
return isSame(root, subRoot) or isSubtree(root.left, subRoot) or isSubtree(root.right, subRoot)
时间复杂度分析:
- 最坏情况下(树退化为链表)为O(mn)
- 平均情况通过提前终止能优化效率
3.3 构造二叉树的分解思路
根据遍历序列重建二叉树(如LeetCode 105、106)的核心在于:
- 确定根节点位置(前序首元素/后序末元素)
- 在中序序列中找到根节点位置
- 递归构建左右子树
以前序+中序为例:
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
优化方向:
- 使用哈希表存储中序索引(避免重复查找)
- 使用指针代替数组切片(减少空间开销)
4. 二叉搜索树专项突破
4.1 BST验证与性质应用
验证BST(LeetCode 98)需要注意避免经典错误:
python复制# 错误写法:仅检查局部父子关系
def isBST(root):
if not root: return True
if root.left and root.left.val >= root.val:
return False
if root.right and root.right.val <= root.val:
return False
return isBST(root.left) and isBST(root.right)
正确做法是传递值域范围:
python复制def isValidBST(root):
def helper(node, lower=float('-inf'), upper=float('inf')):
if not node: return True
if node.val <= lower or node.val >= upper:
return False
return helper(node.left, lower, node.val) and helper(node.right, node.val, upper)
return helper(root)
4.2 BST迭代器设计
BST迭代器(LeetCode 173)考察非递归中序遍历:
python复制class BSTIterator:
def __init__(self, root):
self.stack = []
self._push_left(root)
def _push_left(self, node):
while node:
self.stack.append(node)
node = node.left
def next(self):
node = self.stack.pop()
self._push_left(node.right)
return node.val
def hasNext(self):
return bool(self.stack)
这种实现保证了:
- next()和hasNext()平均时间复杂度O(1)
- 最坏空间复杂度O(h),h为树高
5. 复杂问题拆解技巧
5.1 最近公共祖先问题
LCA问题(LeetCode 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: return root
return left or right
算法原理:
- 如果当前节点是p或q,则返回当前节点
- 如果左右子树分别包含p和q,则当前节点就是LCA
- 否则返回非空的子树结果
5.2 二叉树序列化
序列化(LeetCode 297)通常采用前序+特殊标记:
python复制def serialize(root):
if not root: return '#'
return f"{root.val},{serialize(root.left)},{serialize(root.right)}"
def deserialize(data):
def helper(nodes):
val = next(nodes)
if val == '#': return None
node = TreeNode(int(val))
node.left = helper(nodes)
node.right = helper(nodes)
return node
return helper(iter(data.split(',')))
这种方法的优势是:
- 序列化字符串紧凑
- 重建过程与序列化顺序一致
- 可以处理包含重复值的树
6. 刷题实战建议
6.1 高频题目训练清单
根据出现频率排序的必刷题:
-
- 二叉树的中序遍历
-
- 对称二叉树
-
- 二叉树的层序遍历
-
- 二叉树的最大深度
-
- 从前序与中序遍历序列构造二叉树
-
- 二叉树展开为链表
-
- 二叉树中的最大路径和
-
- 翻转二叉树
-
- 二叉树的最近公共祖先
-
- 二叉树的直径
6.2 调试与验证技巧
在面试白板编码时,建议:
- 先画出示例树的图示
- 手动模拟算法执行过程
- 考虑边界情况:
- 空树
- 单节点树
- 只有左/右子树
- 退化为链表的树
例如验证中序遍历时,可以构造如下测试用例:
python复制# 1
# \
# 2
# /
# 3
# 应输出 [1,3,2]
6.3 时间复杂度分析速查
| 算法类型 | 平均情况 | 最坏情况 | 空间复杂度 |
|---|---|---|---|
| 递归遍历 | O(n) | O(n) | O(h) |
| 迭代遍历 | O(n) | O(n) | O(h) |
| Morris遍历 | O(n) | O(n) | O(1) |
| 路径和问题 | O(n) | O(n²) | O(h) |
| 子树匹配 | O(mn) | O(mn) | O(h) |
| BST验证 | O(n) | O(n) | O(h) |
其中h为树高,在平衡二叉树中h=log(n),最坏情况下h=n
