1. LeetCode Hot100 96-100题核心技巧解析
作为算法面试的黄金标准,LeetCode Hot100汇集了高频考题与经典解法。96-100这五道题看似分散,实则暗藏共性解题模式。我将结合自己刷题3000+的经验,拆解每道题的破题关键与代码优化技巧。
1.1 题目概览与核心考点
96-100题分别是:
-
- 不同的二叉搜索树(动态规划)
-
- 交错字符串(二维DP)
-
- 验证二叉搜索树(树遍历)
-
- 恢复二叉搜索树(Morris遍历)
-
- 相同的树(递归/迭代)
这组题目呈现三大特征:
- 树结构占比60%(96/98/99/100)
- 动态规划占40%(96/97)
- 均需掌握O(1)空间复杂度的解法
关键提示:面试官常在这些题上考察空间复杂度优化,尤其是Morris遍历和滚动数组技巧
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 动态规划专题精讲
2.1 96题:卡特兰数建模
题目要求计算n个节点能组成多少种BST。常规递归解法时间复杂度O(n!)会超时,正确解法是发现其满足卡特兰数递推:
python复制def numTrees(n):
dp = [0]*(n+1)
dp[0], dp[1] = 1, 1
for i in range(2, n+1):
for j in range(1, i+1):
dp[i] += dp[j-1] * dp[i-j]
return dp[n]
优化技巧:
- 内层循环可改为
range(1, i//2 +1)后乘2(利用对称性) - Python可用
math.comb(2n,n)//(n+1)直接计算
2.2 97题:二维DP降维打击
判断s3是否由s1和s2交错组成。经典二维DP方程为:
python复制dp[i][j] = (dp[i-1][j] and s1[i-1]==s3[i+j-1]) or
(dp[i][j-1] and s2[j-1]==s3[i+j-1])
空间优化:使用滚动数组将空间从O(mn)降到O(n)
python复制def isInterleave(s1, s2, s3):
if len(s1) + len(s2) != len(s3): return False
dp = [False]*(len(s2)+1)
dp[0] = True
for j in range(1, len(s2)+1):
dp[j] = dp[j-1] and s2[j-1]==s3[j-1]
for i in range(1, len(s1)+1):
dp[0] = dp[0] and s1[i-1]==s3[i-1]
for j in range(1, len(s2)+1):
dp[j] = (dp[j] and s1[i-1]==s3[i+j-1]) or \
(dp[j-1] and s2[j-1]==s3[i+j-1])
return dp[-1]
3. 二叉树高阶操作
3.1 98题:验证BST的陷阱
常见错误是仅比较节点与左右子节点值。正确做法是记录上下界:
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)
迭代版技巧:中序遍历时检查是否递增
python复制def isValidBST(root):
stack, prev = [], float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
if root.val <= prev: return False
prev = root.val
root = root.right
return True
3.2 99题:Morris遍历实战
恢复被错误交换两个节点的BST。要求O(1)空间时需用Morris遍历:
python复制def recoverTree(root):
x = y = pred = None
while root:
if root.left:
# 找前驱节点
predecessor = root.left
while predecessor.right and predecessor.right != root:
predecessor = predecessor.right
if not predecessor.right:
predecessor.right = root
root = root.left
else:
# 检查逆序对
if pred and root.val < pred.val:
y = root
if not x: x = pred
pred = root
predecessor.right = None
root = root.right
else:
if pred and root.val < pred.val:
y = root
if not x: x = pred
pred = root
root = root.right
x.val, y.val = y.val, x.val
注意事项:Morris遍历会修改树结构,面试时需向面试官说明
4. 代码实现与边界处理
4.1 100题:树的同构判断
判断两棵树是否完全相同,注意处理None节点:
python复制def isSameTree(p, q):
if not p and not q: return True
if not p or not q: return False
return p.val == q.val and \
isSameTree(p.left, q.left) and \
isSameTree(p.right, q.right)
迭代版BFS实现:
python复制from collections import deque
def isSameTree(p, q):
queue = deque([(p, q)])
while queue:
n1, n2 = queue.popleft()
if not n1 and not n2: continue
if not n1 or not n2: return False
if n1.val != n2.val: return False
queue.append((n1.left, n2.left))
queue.append((n1.right, n2.right))
return True
5. 高频问题与优化策略
5.1 时间/空间复杂度权衡
| 题号 | 最优时间复杂度 | 最优空间复杂度 | 关键技巧 |
|---|---|---|---|
| 96 | O(n²) | O(n) | 卡特兰数 |
| 97 | O(mn) | O(n) | 滚动数组 |
| 98 | O(n) | O(h) | 中序遍历 |
| 99 | O(n) | O(1) | Morris遍历 |
| 100 | O(n) | O(h) | 层次遍历 |
5.2 常见错误排查
-
96题错误:
- 误用排列组合公式(实际是组合问题)
- 忘记处理n=0时dp[0]=1的边界条件
-
97题易错点:
- 未先检查长度是否匹配(重要剪枝)
- 二维DP初始化时漏掉dp[0][0]=True
-
98题陷阱:
- 仅验证当前节点与子节点关系
- 使用中序遍历时未正确处理相等值情况
-
Morris遍历调试:
- 忘记恢复树结构导致死循环
- 逆序对检测时漏掉第二个错误节点
6. 刷题进阶建议
-
同类题扩展:
- 96 → 95题(生成所有BST)
- 97 → 115题(不同子序列)
- 98 → 501题(BST众数)
-
代码模板化:
- 树遍历统一使用"标记法"迭代模板
python复制stack = [(root, False)] while stack: node, visited = stack.pop() if visited: # 处理节点 else: # 逆序入栈 stack.append((node, True)) if node.right: stack.append((node.right, False)) if node.left: stack.append((node.left, False)) -
空间优化套路:
- 二维DP → 一维DP(97题)
- 递归 → 迭代(98/100题)
- 栈 → 线索化(99题)
在实际面试中,这组题目常被用作考察候选人代码优化能力的试金石。建议在理解基础解法后,重点练习空间优化版本,并养成先问清楚时空复杂度要求的习惯。对于树相关问题,要特别警惕递归调用栈的空间开销,这在处理超大规模数据时会成为致命瓶颈。
