1. 项目背景与目标设定
作为一名算法刷题爱好者,我给自己定下了每周固定刷题量的目标。3月23日这天的任务清单是:完成LeetCode进阶题库的第45题(跳跃游戏II)和第84题(柱状图中最大的矩形),同时二刷基础题库的第132题(分割回文串II)和第129题(求根节点到叶节点数字之和)。这种混合刷题方式是我在长期实践中总结出的高效训练法——通过新题挑战与旧题重做相结合,既能拓展解题思路,又能巩固已有知识。
选择这四道题目并非偶然。45题和84题都是经典的动态规划/单调栈难题,在面试中出现频率极高;而132题和129题虽然被归类为基础题,但涉及的回文串处理和树形DFS在实际工程中应用广泛。二刷的目的不仅是复习解法,更是要寻找更优的实现方案。这种"2新+2旧"的组合模式,既能保持学习的新鲜感,又能通过重复训练强化肌肉记忆。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解题过程与算法分析
2.1 跳跃游戏II(第45题)
这道题要求找到从数组起始位置到末尾的最少跳跃次数。我的第一版解法采用了贪心算法:
python复制def jump(nums):
jumps = 0
current_end = 0
farthest = 0
for i in range(len(nums)-1):
farthest = max(farthest, i + nums[i])
if i == current_end:
jumps += 1
current_end = farthest
return jumps
关键点在于理解current_end和farthest这两个指针的配合:
current_end标记当前跳跃能到达的边界farthest记录在遍历过程中能到达的最远位置- 当遍历到
current_end时触发跳跃,并更新边界
时间复杂度O(n)的证明:虽然看起来有双重循环,但实际每个元素只被访问一次。空间复杂度O(1)的表现也相当出色。
2.2 柱状图中最大的矩形(第84题)
这道经典难题我尝试了三种解法:
- 暴力法(O(n²)):对每个柱子向左右扩展,计算可能的最大面积
- 分治法(O(nlogn)):找到最小柱子后分治处理左右子区间
- 单调栈(O(n)):最优解法
最终采用的单调栈实现:
python复制def largestRectangleArea(heights):
stack = [-1]
max_area = 0
heights.append(0)
for i in range(len(heights)):
while stack[-1] != -1 and heights[stack[-1]] > heights[i]:
h = heights[stack.pop()]
w = i - stack[-1] - 1
max_area = max(max_area, h * w)
stack.append(i)
return max_area
这里有几个易错点:
- 需要在原始数组末尾添加0作为哨兵值
- 栈中存储的是索引而非高度值
- 宽度计算方式为
i - stack[-1] - 1
3. 二刷题目精进
3.1 分割回文串II(第132题)
第一次做这道题时我用了DFS+剪枝,时间复杂度较高。这次改用动态规划优化:
python复制def minCut(s):
n = len(s)
dp = [i-1 for i in range(n+1)]
is_pal = [[False]*n for _ in range(n)]
for j in range(n):
for i in range(j+1):
if s[i] == s[j] and (j-i<=1 or is_pal[i+1][j-1]):
is_pal[i][j] = True
dp[j+1] = min(dp[j+1], dp[i]+1)
return dp[n]
关键优化点:
- 预处理回文判断矩阵is_pal
- dp数组表示前i个字符的最小分割次数
- 时间复杂度从O(2^n)降到O(n²)
3.2 求根节点到叶节点数字之和(第129题)
二刷时我发现之前的解法没有充分利用Python的语言特性,改进后的DFS更简洁:
python复制def sumNumbers(root):
def dfs(node, path_sum):
if not node:
return 0
path_sum = path_sum * 10 + node.val
if not node.left and not node.right:
return path_sum
return dfs(node.left, path_sum) + dfs(node.right, path_sum)
return dfs(root, 0)
优化亮点:
- 去掉了不必要的临时变量
- 直接返回递归结果而非累积到外部变量
- 路径和计算融入参数传递
4. 刷题方法论与工具链
4.1 个人刷题工作流
我建立了完整的刷题管理系统:
- 题目分类:按类型(数组/树/DP等)和难度分级
- 解题记录:每个题目保留多个版本的代码
- 错题本:记录错误原因和优化思路
- 周期性复习:使用Anki进行间隔重复
4.2 效率提升技巧
- 计时训练:简单题15分钟,中等题30分钟,难题60分钟
- 三色标记法:
- 绿色:完全掌握
- 黄色:思路正确但实现有瑕疵
- 红色:完全没思路
- 白板编程:模拟面试环境练习
4.3 常用工具推荐
- LeetCode官方解题模板生成器
- VisuAlgo算法可视化工具
- Python Tutor代码执行可视化
- Draw.io画图辅助理解复杂指针操作
5. 常见问题与解决方案
5.1 边界条件处理
在解决84题时,我最初忽略了空输入的情况。现在养成的习惯是:
- 显式处理空数组/空树等特殊情况
- 在代码开头添加防御性检查
- 编写单元测试覆盖边界条件
5.2 调试技巧
对于复杂的指针操作(如单调栈):
- 在关键节点打印变量状态
- 使用小规模测试用例(3-5个元素)
- 画图辅助理解指针移动
5.3 性能优化
从暴力解法到最优解法的思考路径:
- 先写出能工作的代码
- 分析时间/空间复杂度
- 寻找重复计算的部分
- 考虑是否能用记忆化或DP优化
- 最终考虑更高级的数据结构
6. 进阶训练建议
对于已经掌握基础算法的同学,我建议:
- 尝试一题多解(如84题的三种解法)
- 参与周赛锻炼临场发挥能力
- 阅读顶级选手的解题报告
- 尝试用不同语言实现同一算法
在刷完这四道题后,我明显感觉到对指针操作和动态规划的理解更加深入了。特别是二刷时发现之前忽略的细节,这种"温故知新"的体验正是持续进步的关键。
