1. LeetCode精选56-100题解精要
刷算法题是程序员提升编码能力的必经之路,而LeetCode作为全球知名的算法题库,其精选题目更是面试中的常客。最近我系统刷完了56-100题,这些题目覆盖了数组、字符串、链表、树、动态规划等核心数据结构与算法。不同于简单的AC(Accepted),我更关注每道题目的最优解法和底层逻辑。
2. 高频题型与解题框架
2.1 数组与双指针技巧
LeetCode 56(合并区间)和57(插入区间)是典型的区间处理问题。这类问题的核心在于:
- 按起始位置排序区间
- 维护当前合并区间[start, end]
- 遍历时比较新区间与当前区间的关系
python复制def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
关键点:排序是O(nlogn)时间复杂度,合并过程是O(n),注意边界条件的处理。
2.2 二叉树遍历的进阶应用
LeetCode 94(中序遍历)、102(层序遍历)和105(从前序与中序遍历构造二叉树)考察了对二叉树遍历的深入理解。非递归实现需要熟练掌握栈的应用:
python复制def inorderTraversal(root):
res = []
stack = []
curr = root
while curr or stack:
while curr:
stack.append(curr)
curr = curr.left
curr = stack.pop()
res.append(curr.val)
curr = curr.right
return res
对于构造二叉树问题,核心在于:
- 前序数组的第一个元素是根节点
- 在中序数组中找到根节点位置
- 递归构建左右子树
3. 动态规划经典问题剖析
3.1 背包问题变种
LeetCode 96(不同的二叉搜索树)是典型的动态规划问题。解题要点:
- 定义dp[i]表示i个节点能组成的BST数量
- 对于每个i,枚举j作为根节点,则左子树有j-1个节点,右子树有i-j个节点
- 状态转移方程:dp[i] += dp[j-1] * dp[i-j]
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]
3.2 字符串处理与DP
LeetCode 72(编辑距离)是字符串DP的经典问题。定义dp[i][j]表示word1前i个字符转换成word2前j个字符的最小操作数。需要考虑三种操作:
- 插入:dp[i][j] = dp[i][j-1] + 1
- 删除:dp[i][j] = dp[i-1][j] + 1
- 替换:dp[i][j] = dp[i-1][j-1] + (0 if word1[i-1]==word2[j-1] else 1)
4. 图论算法实战
4.1 并查集应用
LeetCode 200(岛屿数量)可以用DFS或并查集解决。并查集实现要点:
- 初始化时将每个'1'的节点父节点设为自己
- 遍历时合并相邻的'1'节点
- 最后统计根节点数量
python复制class UnionFind:
def __init__(self, grid):
m, n = len(grid), len(grid[0])
self.count = 0
self.parent = [0] * (m * n)
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
self.parent[i * n + j] = i * n + j
self.count += 1
def find(self, i):
if self.parent[i] != i:
self.parent[i] = self.find(self.parent[i])
return self.parent[i]
def union(self, x, y):
rootx = self.find(x)
rooty = self.find(y)
if rootx != rooty:
self.parent[rootx] = rooty
self.count -= 1
4.2 拓扑排序
LeetCode 207(课程表)是典型的拓扑排序问题。解题步骤:
- 构建邻接表和入度数组
- 将入度为0的节点加入队列
- 依次处理队列中的节点,更新相邻节点的入度
- 最后检查是否所有节点都被处理
5. 代码优化与调试技巧
5.1 时间复杂度分析
对于递归算法,要学会画递归树分析时间复杂度。例如LeetCode 95(不同的二叉搜索树II),暴力解法的时间复杂度是卡特兰数O(4^n/n^(3/2)),需要通过记忆化优化。
5.2 边界条件处理
常见边界陷阱包括:
- 数组为空或长度为1的情况
- 整数运算的溢出问题(特别是Python2到Python3的迁移)
- 树节点为None时的处理
- 字符串的空格和特殊字符
调试建议:对于树问题,先手动构建测试用例;对于DP问题,打印出DP表格验证状态转移。
6. 刷题策略与面试准备
- 按专题刷题:先掌握每个数据结构的标准操作,再解决变种问题
- 二刷重点题目:标记第一次做时卡壳的题目,间隔一周后重新实现
- 模拟面试环境:使用白板或纯文本编辑器,限制解题时间
- 总结模板代码:如快速排序、Dijkstra算法等高频考点的标准实现
我个人的刷题节奏是每天3-5题,周末集中复习。对于每道题目,会记录以下信息:
- 初始思路和最终解法的时间/空间复杂度
- 相关的相似题目(如LeetCode 115之于72题)
- 容易出错的测试用例
- 可能的follow-up问题
经过这套题目的训练,最大的收获是对动态规划的状态定义更加敏感,能够快速识别子问题重叠的特征。对于树和图的问题,也逐渐养成了先画图再编码的习惯。