1. 三月做题记录:程序员的算法精进之路
三月的键盘敲击声里,总伴随着LeetCode提交页面的刷新音效。作为从业八年的全栈工程师,我依然保持着每月至少20道算法题的训练强度——这不是为了应付面试,而是对抗技术惰性的最佳武器。本文将完整呈现我的三月刷题清单,包含精心挑选的15道经典题型和5道周赛新题,涵盖动态规划、图论、数据结构等核心领域,每道题都附上我的解题思路、优化过程和实战踩坑记录。
2. 题目筛选策略与分类体系
2.1 阶梯式难度分布
三月题库采用"5-7-3"的金字塔结构:5道基础题巩固模板(如二分查找、链表反转),7道中等题训练思维(如区间合并、拓扑排序),3道hard题突破瓶颈(如数位DP、线段树应用)。这种分布既能保持手感,又能持续提升解题能力。
注意:新手建议调整为"8-5-2"的比例,避免过早接触hard题导致挫败感
2.2 题型覆盖矩阵
我使用自建的题型检查表确保全面覆盖:
| 类别 | 基础题 | 进阶题 | 挑战题 |
|---|---|---|---|
| 动态规划 | 爬楼梯 | 最长递增子序列 | 正则表达式匹配 |
| 图论 | 岛屿数量 | 课程表II | 最小体力消耗路径 |
| 数据结构 | 有效的括号 | LRU缓存 | 数据流中位数 |
3. 核心解题模式深度解析
3.1 动态规划的三层突破
以经典题"322. 零钱兑换"为例,我的解题日志记录了三阶段进化:
- 暴力递归版(初始思路)
def coinChange(coins, amount): if amount == 0: return 0 min_coins = float('inf') for coin in coins: if amount - coin >= 0: res = coinChange(coins, amount - coin) if res != -1: min_coins = min(min_coins, res + 1) return min_coins if min_coins != float('inf') else -1时间复杂度:O(S^n) ,S为金额,n为硬币种类
- 备忘录优化(添加缓存)
memo = {} def coinChange(coins, amount): if amount in memo: return memo[amount] # ...其余逻辑同暴力版... memo[amount] = min_coins if min_coins != float('inf') else -1 return memo[amount]时间复杂度降至O(S*n)
- DP Table终极版
def coinChange(coins, amount): dp = [float('inf')] * (amount + 1) dp[0] = 0 for i in range(1, amount+1): for coin in coins: if i >= coin: dp[i] = min(dp[i], dp[i-coin]+1) return dp[amount] if dp[amount] != float('inf') else -1空间复杂度优化到O(S)
3.2 图论算法的实战技巧
在解决"787. K站中转内最便宜的航班"时,我总结了Bellman-Ford算法的几个关键点:
- 松弛操作次数:K次中转意味着需要执行K+1轮松弛
- 临时数组必要性:必须使用临时数组存储上一轮结果,防止同一轮多次松弛
- 提前终止条件:当某轮松弛未更新任何值时可直接返回
优化后的代码实现:
def findCheapestPrice(n, flights, src, dst, k): prices = [float('inf')] * n prices[src] = 0 for _ in range(k + 1): tmp = prices.copy() updated = False for u, v, w in flights: if prices[u] + w < tmp[v]: tmp[v] = prices[u] + w updated = True prices = tmp if not updated: break return prices[dst] if prices[dst] != float('inf') else -14. 高频错题本与Debug实录
4.1 边界条件陷阱
"29. 两数相除"这道medium题让我栽了三次跟头:
- 溢出处理:当被除数为-2³¹,除数为-1时,结果2³¹会溢出
- 符号处理:不能直接取绝对值计算,因为-2³¹取绝对值会溢出
- 加速技巧:使用指数增长搜索(每次将除数翻倍)时,要注意剩余量可能小于当前除数
最终通过的解决方案:
def divide(dividend, divisor): INT_MIN, INT_MAX = -2**31, 2**31 - 1 if dividend == INT_MIN and divisor == -1: return INT_MAX negative = (dividend < 0) != (divisor < 0) dividend, divisor = abs(dividend), abs(divisor) result = 0 while dividend >= divisor: temp, multiple = divisor, 1 while dividend >= (temp << 1): temp <<= 1 multiple <<= 1 dividend -= temp result += multiple return -result if negative else result4.2 数据结构选择误区
在"239. 滑动窗口最大值"中,我最初尝试用大顶堆实现:
def maxSlidingWindow(nums, k): heap = [(-nums[i], i) for i in range(k)] heapq.heapify(heap) result = [-heap[0][0]] for i in range(k, len(nums)): heapq.heappush(heap, (-nums[i], i)) while heap[0][1] <= i - k: heapq.heappop(heap) result.append(-heap[0][0]) return result时间复杂度:O(nlogk)
后发现单调队列可以实现O(n):
def maxSlidingWindow(nums, k): from collections import deque q = deque() result = [] 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: result.append(nums[q[0]]) return result5. 周赛题目速攻策略
三月第四周周赛的压轴题"2242. 节点序列的最大得分"展示了图论问题的典型解题框架:
- 问题转化:将节点序列得分转化为寻找长度为4的路径最大权重和
- 邻接表预处理:构建每个节点的Top3邻居列表(按权重降序)
- 四重循环优化:通过提前剪枝减少计算量
关键实现片段:
def maximumScore(scores, edges): from collections import defaultdict graph = defaultdict(list) for u, v in edges: graph[u].append((scores[v], v)) graph[v].append((scores[u], u)) for i in graph: graph[i].sort(reverse=True) graph[i] = graph[i][:3] # 只保留前三大的邻居 max_score = -1 for u in graph: for (score_v, v) in graph[u]: for (score_w, w) in graph[v]: if w == u: continue for (score_x, x) in graph[w]: if x == u or x == v: continue max_score = max(max_score, scores[u]+scores[v]+scores[w]+scores[x]) return max_score6. 刷题环境配置与效率工具
6.1 本地测试框架
我使用pytest搭建的自动化测试环境,模板如下:
import pytest from solution import Solution @pytest.mark.parametrize("nums, target, expected", [ ([2,7,11,15], 9, [0,1]), ([3,2,4], 6, [1,2]), ]) def test_twoSum(nums, target, expected): sol = Solution() assert sol.twoSum(nums, target) == expected6.2 性能分析技巧
对于时间复杂度存疑的解法,我使用cProfile进行验证:
import cProfile def test_performance(): # 测试代码... cProfile.run('test_performance()', sort='cumtime')7. 下月计划与专项突破
根据三月暴露的薄弱环节,四月将重点攻坚:
- 数位DP专题:针对"233. 数字1的个数"类问题
- 线段树应用:解决区域和检索问题
- 博弈论问题:如"292. Nim游戏"的变种
我的个人经验是,持续记录解题过程中的思维盲点比单纯追求题量更重要。当你在某类题型上反复犯错时,往往意味着这里有真正的知识缺口需要填补。