1. 项目背景与核心价值
作为一名算法工程师,我坚持每天记录自己的刷题过程已经超过三年。这份"2026-03-10~12 hetao1733837的刷题记录"是我日常训练的一个典型切片,记录了连续三天内解决的算法问题及其思考过程。不同于普通的刷题列表,这份记录包含了题目分析、多种解法的比较、优化思路以及实际编码中的调试心得。
在技术面试越来越注重实际解决问题能力的今天,系统性的刷题训练已成为程序员职业发展的必经之路。但很多人在刷题过程中容易陷入"只求AC(Accepted)"的误区,忽视了思维过程和优化方法的记录。这正是我坚持详细记录的价值所在——它不仅帮助我巩固知识点,更形成了可追溯的技术成长轨迹。
2. 刷题方法论与记录体系
2.1 题目选择策略
我采用分层递进的选题方式:
- 每日1道困难题(如LeetCode Hard)
- 2-3道中等题(侧重不同算法类型)
- 1道之前做错或未完全理解的复习题
这种组合既能保持挑战性,又能巩固基础。以3月10日为例:
- 新题:LC 218 天际线问题(扫描线算法)
- 复习题:LC 76 最小覆盖子串(滑动窗口优化)
2.2 记录模板设计
每道题的记录包含以下核心字段:
## [日期] [题号] 题目名称 **标签**:算法分类(如DFS、DP) **初始思路**:第一直觉解法 **复杂度分析**:时间/空间复杂度估算 **优化过程**:逐步改进的思路 **最终代码**:带注释的实现 **总结**:关键收获与待改进点例如3月11日记录的LC 239滑动窗口最大值问题:
# 单调队列解法 from collections import deque class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: q = deque() # 存储下标而非值 res = [] for i, num in enumerate(nums): while q and nums[q[-1]] <= num: # 维护单调递减 q.pop() q.append(i) if q[0] == i - k: # 移除越界元素 q.popleft() if i >= k - 1: res.append(nums[q[0]]) return res2.3 知识图谱构建
我会用Notion建立算法知识图谱,将题目与以下维度关联:
- 算法类型(动态规划、图论等)
- 企业真题频率(根据面经统计)
- 个人掌握程度(1-5星评分)
- 相似题目关联(如背包问题的变种)
这种结构化记录使得复习时可以按知识模块进行针对性训练,而非随机刷题。
3. 典型题目深度解析
3.1 天际线问题(LC 218)
问题描述: 给定建筑物的起止坐标和高度,输出城市天际线的关键点坐标。
解法演进:
- 暴力解法(O(n^2)):遍历所有x坐标,计算每个位置的最大高度
- 扫描线优化(O(nlogn)):
- 将建筑物拆解为左右边界事件点
- 使用最大堆维护当前高度
- 关键点出现在当前最大高度变化时
代码实现要点:
import heapq def getSkyline(buildings): events = [] for L, R, H in buildings: events.append((L, -H, R)) # 用负高度区分左右 events.append((R, 0, 0)) # 右边界 events.sort() res = [] heap = [(0, float('inf'))] # (高度, 右边界) for x, negH, R in events: while heap[0][1] <= x: # 弹出过期的建筑物 heapq.heappop(heap) if negH: heapq.heappush(heap, (negH, R)) if not res or res[-1][1] != -heap[0][0]: res.append([x, -heap[0][0]]) return res调试心得:
- 边界事件处理:右边界高度设为0以保证正确弹出
- 堆的维护:需要同时存储右边界坐标
- 去重逻辑:只有当最大高度变化时才记录关键点
3.2 最小覆盖子串(LC 76)
滑动窗口优化过程:
- 初始暴力解法:枚举所有子串,检查是否包含目标字符(O(n^3))
- 基础滑动窗口:维护左右指针(O(n))
- 优化技巧:
- 使用counter记录字符需求
- 额外变量记录满足条件的字符数
- 前移左指针时跳过无关字符
性能对比:
| 方法 | 时间复杂度 | 实际运行时间(ms) |
|---|---|---|
| 暴力 | O(n^3) | >3000 (TLE) |
| 基础滑动窗口 | O(n) | 120 |
| 优化滑动窗口 | O(n) | 48 |
4. 效率提升实战技巧
4.1 调试与验证方法
小数据测试法:
- 先用手算验证简单case
- 例如测试滑动窗口问题时:
print(Solution().minWindow("ADOBECODEBANC", "ABC")) # 应输出"BANC"
边界条件检查清单:
- 空输入
- 极值情况(如最大数据量)
- 重复元素处理
- 正负零值
可视化调试: 对于图论问题,可以用ASCII画图辅助理解:
0 —— 1 | \ | 2 3
4.2 常见优化模式
空间换时间:
- 预计算前缀和
- 记忆化搜索
- 查表法
双指针技巧:
- 快慢指针(链表问题)
- 左右指针(数组问题)
- 滑动窗口(子串问题)
位运算优化:
- 使用掩码代替集合
- 异或找唯一数
- 位计数技巧
4.3 个人效率工具链
本地测试框架:
import unittest class TestSolutions(unittest.TestCase): def test_skyline(self): self.assertEqual(getSkyline([[2,9,10],[3,7,15]]), [[2,10],[3,15],[7,10],[9,0]]) if __name__ == '__main__': unittest.main()性能分析工具:
import cProfile cProfile.run('Solution().maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)')代码片段管理: 使用VS Code的Code Snippets功能保存常用模板:
{ "Binary Search": { "prefix": "bisect", "body": [ "left, right = 0, len(nums)-1", "while left <= right:", " mid = left + (right-left)//2", " if nums[mid] == target:", " return mid", " elif nums[mid] < target:", " left = mid + 1", " else:", " right = mid - 1", "return -1" ] } }
5. 刷题记录的价值延伸
5.1 面试复盘系统
我将刷题记录与面试经历关联,形成以下分析维度:
- 题目出现频率统计
- 个人解题时间分布
- 错误类型归类(边界条件、算法选择等)
- 企业出题偏好分析
5.2 技术博客素材
精选典型题目记录加工为技术文章,例如:
- 《从暴力解法到最优解:滑动窗口问题的四层进阶》
- 《如何用扫描线算法解决几何问题》
- 《动态规划的降维优化技巧》
5.3 个人能力雷达图
基于刷题数据生成技能评估:
%% 注意:实际使用时需替换为表格形式 radarChart title 算法能力评估 axis 数据结构, 动态规划, 图论, 搜索, 数学 "当前" : 85, 70, 65, 80, 60 "目标" : 90, 85, 75, 85, 70(注:此处mermaid图表仅为示意,实际记录中使用表格代替)
| 技能维度 | 当前水平 | 目标水平 |
|---|---|---|
| 数据结构 | 85 | 90 |
| 动态规划 | 70 | 85 |
| 图论算法 | 65 | 75 |
| 搜索算法 | 80 | 85 |
| 数学相关 | 60 | 70 |
6. 持续改进方向
经过三年多的刷题实践,我发现以下几个关键改进点:
刻意练习:不再追求题目数量,而是针对薄弱环节进行专题突破。比如最近两周集中攻克了10道树形DP问题。
错题重做:建立错题本,对曾经做错的题目定期重做。统计显示第二次做题的正确率能提高40%以上。
模拟面试:使用Pramp等平台进行模拟面试,适应在时间压力下的解题状态。真实面试环境下解题速度比平时慢30%左右。
代码审查:定期review自己三个月前的代码,会发现很多可以优化的地方。比如最近重看之前的回溯代码,发现有大量可以剪枝的优化点。
教学相长:在LeetCode讨论区解答他人问题,这个过程常常能发现自己理解上的盲区。教是最好的学。