☰
力扣每日一题一周复盘:BFS、栈、二分、双指针、单调队列全覆盖
2026/10/13 4:14:22 网站建设 项目流程

从 1 月 19 日到 1 月 25 日,这一周的 LeetCode 每日一题我完整跟了一遍。项目标题听起来只是“日历打卡”,实际上这一周过得相当充实:既有 994 腐烂的橘子这样的 BFS 经典,又有 224 基本计算器这种让人头皮发麻的表达式求值,中途还穿插了狒狒爱吃香蕉、接雨水、滑动窗口最大值这些高频常客,周六又撞上周赛 430。如果你正处于准备面试的阶段,想找一个“既上强度又覆盖核心题型”的七天样例,这一周的题目安排就很值得拆开研究。

这篇文章我会把每天的题都拉出来过一遍,讲清楚每题背后的算法原型、代码怎么落地、边界在哪里,再补上我实际操作中踩过的坑。最后一part也放了连续刷题的方法论,适合刚入坑 LeetCode 的人收藏着看。我保证不只是贴代码,而是把“为什么这么写”说透。

1. 本周每日一题盘点:七天七种套路

1.1 这一周的具体安排

先把题目列出来,方便你对号入座:

日期题目核心标签一句话点评
1月19日(周一)腐烂的橘子(994)多源 BFS图遍历的入门试金石,层数计数写错会全盘崩
1月20日(周二)基本计算器(224)栈、括号展开表达式类题目的鼻祖,细节极其多
1月21日(周三)狒狒爱吃香蕉(875 / 剑指 Offer II 073)二分答案最标准的二分查找模板题,考“最小速度”
1月22日(周四)接雨水(42)双指针、前后缀最大值热门 100 题里的常青树,思路多样
1月23日(周五)滑动窗口最大值(239)单调队列队列维护的是索引,不是值,理解了就通
1月24日(周六)周赛 430综合四道题一套组合拳,适合检验周中积累
1月25日(周日)复盘 / 补题综合我用来做错题沉淀和重写题解

周一到周五基本是按照“算法思想”铺开的:图论 → 栈 → 二分 → 双指针/前缀最值 → 单调队列。每一种思想之间没有重复,意味着你每天都要换一套思维模式。这种“换脑子”的安排其实比连续刷十道同类型的题更有价值,因为真实面试和竞赛不会提前告诉你这题要用哪个算法。

1.2 为什么这个组合值得写一篇复盘

说实话,之前连续几周的每日一题经常是“变着法子出新题”,难度忽高忽低,每周都暴露出不少知识盲区。这次的好处在于:每一道都是原汁原味的经典题,覆盖面正好是算法面试里出现频率最高的几个方向。我可以负责任地说,如果你能把这一周刷透,LeetCode 热门 100 题里至少有三四十道题你已经摸到门了。

还有个值得注意的点是“同类型题目的纵向对比”:人家的每日一题可能选两到三个相似的题放在相邻几天里帮你巩固,但这次硬生生把五种算法的题放在同一周里。你写完腐烂的橘子,第二天就要切换栈和括号思维,这种突如其来的“跳频道”恰好模拟了周赛和真实面试的状态。我在周赛 430 里就明显感觉到,前五天练的“快速识别题型”能力是能迁移的:拿到题先判断是 BFS、二分还是单调栈,比闷头写代码重要得多。

2. 腐烂的橘子(994):多源 BFS 与层级计时的底层逻辑

2.1 一眼识别“多源 BFS”

题目什么意思我不再重复,简单概括就是:一个二维网格里有新鲜橘子、烂橘子和空格,每一轮烂橘子会把上下左右相邻的新鲜橘子感染变烂,问多少轮之后全部腐烂。这是个很典型的“状态的扩散”问题,而且扩散速度恒定为一轮一个格子。遇到这种“从多个起点同步向外扩散”的问题,第一反应必须是 BFS 而不是 DFS,原因很直接:

  • BFS 天然按层扩展,每一层对应一轮感染,层数就是要求的分钟数;
  • DFS 需要额外维护路径和访问顺序,很难表达“同时感染”的并发语义;
  • 多个初始烂橘子起点互不干扰,用队列把它们全部入队,就是多源 BFS。

这里有个生活化的类比:一堆火源同时着火了,火势每秒向四周蔓延一格,你要算多久烧完一片森林。如果用一个 DFS 去模拟“先沿着一条路走到黑”,你会得到完全错误的蔓延时间,因为现实中所有火源是同步在烧的。队列天然尊重这种同步性。

2.2 可复用的 Python 模板

先看代码,这套模板可以直接背:

from collections import deque from typing import List class Solution: def orangesRotting(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) q = deque() fresh = 0 # 第一步:把所有初始烂橘子入队,同时统计新鲜橘子数量 for i in range(m): for j in range(n): if grid[i][j] == 2: q.append((i, j)) elif grid[i][j] == 1: fresh += 1 # 特判:如果根本没有新鲜橘子,不需要任何分钟 if fresh == 0: return 0 minutes = 0 dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)] # 第二步:逐层 BFS,一层代表一分钟 while q: minutes += 1 for _ in range(len(q)): x, y = q.popleft() for dx, dy in dirs: nx, ny = x + dx, y + dy if 0 <= nx < m and 0 <= ny < n and grid[nx][ny] == 1: grid[nx][ny] = 2 fresh -= 1 q.append((nx, ny)) # 如果某一轮结束之后新鲜橘子已经清零,直接返回 if fresh == 0: return minutes # BFS 结束还有剩余新鲜橘子,说明它们被隔离了,永远无法被感染 return -1

注意一个细节:minutes是在进入当前层时先加一。因为我用的是“先加时间再处理本层”的逻辑,所以不会出现多算一分钟的情况。如果你习惯用 distance 数组记录每个格子的腐烂时刻,那也是常见解法,核心思想和这个一样,只是代码略长。

时间复杂度 O(mn),因为每个格子最多入队一次;空间复杂度 O(mn),主要是队列和最坏情况下的格子数量。

2.3 我踩过的两个边界坑

第一个坑是“无新鲜橘子”的特判。很多人一上来就把所有烂橘子入队开始 BFS,最后发现输入是[[0,2]]时返回了-1。正确语义是:一开始就没有新鲜橘子,需要 0 分钟。所以上面的代码在 BFS 之前先做了if fresh == 0: return 0,这个特判不能省。

第二个坑是“新鲜橘子计数放在 BFS 外边还是循环里”。我见过有人每次都重新扫描全网格找新鲜橘子,变成了 O(mn) 次扫描复杂度相乘,最后提交超时。正确做法是维护一个全局fresh计数,感染一个就减一,BFS 结束时看它是否大于 0。这是一个非常典型的“用变量代替重复遍历”的优化思路,在后期很多复杂题里都会复用。

3. 基本计算器(224):栈、符号与括号展开

3.1 只有“加号和减号”反而更考细节

和 227 基本计算器 II 相比,224 只包含加减和括号,没有乘除,表面上难度降了一截。但真正做过的人都知道,括号带来的符号翻转才是最大的坑。例如1-( -2)这种表达式,一眼看去似乎是两个减号叠在一起,但如果你用“当前符号 sign”的思维去处理,它其实并不复杂。

这里的核心技巧是:不要试图“分析括号内部”,而是把括号看作一个“重置现场”的信号。当遇到(时,把当前已经算好的结果和当前符号压入栈中,然后重置结果和符号,开始计算括号内的表达式;当遇到)时,从栈里弹出之前的符号,用它乘以括号内的计算结果,再加上之前已经算好的结果。这个过程说白了就是“先算局部,再把局部合并进全局”。

我自己的理解是:栈里存的不是数字,而是“上下文”。这两条上下文按顺序分别是进入括号之前已经累计的结果,和进入括号之前的正负号。只要你明白这一点,写代码时就不会把压栈顺序记混。

3.2 关键代码与手动推演

下面是很经典的写法:

class Solution: def calculate(self, s: str) -> int: n = len(s) sign = 1 # 当前符号 res = 0 # 当前累计结果 stack = [] # 用来保存括号前的上下文 i = 0 while i < n: ch = s[i] if ch.isdigit(): num = 0 while i < n and s[i].isdigit(): num = num * 10 + int(s[i]) i += 1 res += sign * num i -= 1 elif ch == '+': sign = 1 elif ch == '-': sign = -1 elif ch == '(': stack.append(res) stack.append(sign) res = 0 sign = 1 elif ch == ')': res = stack.pop() * res + stack.pop() i += 1 return res

拿1-( -2 )手推一遍:

  • 遇到1,res = 1;
  • 遇到-,sign = -1;
  • 遇到(,压入1和-1,重置res = 0、sign = 1;
  • 遇到-,sign = -1;
  • 遇到2,res = -2;
  • 遇到),执行res = sign栈顶 * res + res栈顶 = (-1) * (-2) + 1 = 3。

因此最终答案是 3。很多人想不通为什么1-( -2 )等于 3,其实你不需要在字符串层面处理“两个减号变成加号”,只需要让括号内的2被外层符号再翻转一次,数学上自然就对了。

3.3 从 224 到 227、772 的拓展

刷完 224 之后,建议立刻做 227 基本计算器 II。那个题加入了乘除,而且没有括号,考察的是运算符优先级。核心思路从“一个符号变量”升级成“两个栈”:一个数字栈,一个运算符栈,遇到乘除就立刻计算,遇到加减则先压入栈,等表达式扫描完再从栈顶依次弹出。很多人在 227 里栽在减法顺序上,比如2-3-4如果从右往左弹栈会得到错误结果2-(3-4)。解决办法是让最后弹栈时用左操作数减去右操作数,或者干脆在入栈时用负数表示减法。

再往上是 772 基本计算器 III,那就是既有括号又有乘除,还可能有变量,难度直逼竞赛题。我在实际刷题时给 224 的定位是“必须 15 分钟内写出来的范围”,因为它在面试真题池里出现频率太高了。如果连 224 的压栈/弹栈顺序都要现场推导,那面试压力会非常大。我的建议是:至少把 224 和 227 背到肌肉记忆,再拿到 772 去做拓展开脑洞。

4. 狒狒爱吃香蕉(875 / 剑指 Offer II 073):二分答案的标准模板

4.1 把“求速度”变成“验证可行性”

狒狒爱吃香蕉是另一道“看起来不像二分,实际上就是二分”的题。它的描述比较绕:狒狒一小时只能吃一堆香蕉,若一堆吃完就等着下一小时,求能在 h 小时内吃完所有香蕉的最小速度 k。

你拿到题首先要想清楚:如果速度 k 很小,需要的小时数就多;如果 k 很大,需要的小时数就少。也就是说,随着 k 增大,“能否在 h 小时内吃完”这个结果是单调的:从不可能变为可能,而且一旦可能,更大的 k 一定也可行。这种“单调性是二分法能成立的根本前提”。

那么问题就变成了:在速度区间里找到一个最小的可行值。这和我们平时做“在有序数组中查找第一个满足条件的元素”完全一样,只不过 check 函数不再是简单的比较大小,而是计算“用速度 k 吃完所有香蕉需要多少小时”。

我经常用一个类比:你要在一个范围内猜一个最低出价能让供应商接受,猜高了虽然能成交但多花钱,猜低了供应商不发货。所以每次取中间价去验证,高了就往下压,低了就往上涨。这就是二分答案的思想,LeetCode 里大量“最大化最小值”“最小化最大值”都是这个套路。

4.2 二分区间与 check 函数的精确实现

来看代码:

from typing import List class Solution: def minEatingSpeed(self, piles: List[int], h: int) -> int: # check 函数:速度 k 时吃完所有香蕉需要的小时数 def feasible(k: int) -> bool: hours = 0 for p in piles: hours += (p + k - 1) // k return hours <= h left, right = 1, max(piles) while left < right: mid = (left + right) // 2 if feasible(mid): right = mid else: left = mid + 1 return left

最妙的部分其实是(p + k - 1) // k,这是一个“向上取整除法”的经典写法。比math.ceil(p / k)更快,也避免了浮点数精度问题。比如一堆香蕉有 10 根,速度 k=3,向上取整需要 4 小时,直接(10 + 3 - 1) // 3 = 12 // 3 = 4,结果正确。

二分区间为什么是[1, max(piles)]?因为每小时最多只能吃掉一堆,速度再快也不会快过“一堆一小时”,所以 k 取最大值max(piles)时,正好每堆一小时,总时间是len(piles)。由于题目保证h >= len(piles),这个上界必然可行。如果你不确定题目有没有这个保证,可以把右边界设成sum(piles),虽然会稍微增加二分次数,但也完全没问题。

4.3 二分模板的几个易错点

第一,左边界不能是 0。p = 0时除零直接报错。第二,二分条件要用feasible(mid)判断“是否够用”,如果够用就往左收,不够用就往右收。这个模板是标准的“寻找左边界”写法,和“寻找右边界”的if nums[mid] <= target: left = mid有点不同,不要混。第三,很多人会把hours <= h写成< h,导致答案永远差一,边界题直接 WA。一定要读清题目:如果 h=8,正好 8 小时吃完是可行的,是小于等于,不是小于。

5. 热门 100 题精选:接雨水与滑动窗口最大值的双路口

5.1 接雨水(42):从暴力到双指针

接雨水是 LeetCode 热门 100 题里我用红色标记的经典,因为它解法层次特别丰富:暴力、前缀/后缀最大值、双指针、单调栈都能做,而且每种解法背后的思维都能延伸到其他题。

先说暴力:每个格子能接多少水,取决于它左右两侧最高柱子的较小值减去自身高度,即:

ans += min(leftMax[i], rightMax[i]) - height[i]

但每格都扫左右两侧就是 O(n²),不可取。于是出现前后缀数组做法:先用两个数组预处理每个位置左侧最高值和右侧最高值,再扫一遍累计。这是 O(n) 时间、O(n) 空间,思路最容易理解。

双指针做法则是把空间压到 O(1)。核心观察是:在左右两个指针相遇的过程中,某一侧的高度更矮,那么这一侧当前格子的水量就可以确定。因为水量取决于较低的一边。代码如下:

from typing import List class Solution: def trap(self, height: List[int]) -> int: n = len(height) if n <= 2: return 0 left, right = 0, n - 1 left_max, right_max = 0, 0 ans = 0 while left < right: if height[left] < height[right]: if height[left] >= left_max: left_max = height[left] else: ans += left_max - height[left] left += 1 else: if height[right] >= right_max: right_max = height[right] else: ans += right_max - height[right] right -= 1 return ans

这里有个容易绕晕的点:为什么左侧柱更矮时只更新left_max,而不需要考虑right_max?因为右边有更高的柱子兜底,当前格子的水位上限就取决于左侧已经扫过的最大高度。反之亦然。一旦理解“矮的一侧先结算”,代码其实是水到渠成的。

5.2 滑动窗口最大值(239):单调队列的精髓

滑动窗口最大值是“固定窗口 + 求最值”的经典题。暴力写法是每滑动一次就遍历窗口内 k 个元素,复杂度 O(nk),数据一大直接超时。更高效的做法是单调队列,也就是在一个双端队列里维护一组“下标递增、值递减”的候选值。

什么意思?假设队列头永远是目前窗口最大值的下标,当你加入一个新元素时,从队尾弹出所有比它小的元素,因为它们永远不可能是最大值了。这个操作保证队列里的值从队头到队尾是递减的。每次窗口移动时,再把队头中已经滑出窗口的过期下标弹出。最后取队头就是答案。

from collections import deque from typing import List class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: q = deque() res = [] for i, x in enumerate(nums): # 1. 清理过期下标 while q and q[0] <= i - k: q.popleft() # 2. 维护单调递减 while q and nums[q[-1]] <= x: q.pop() q.append(i) # 3. 窗口完整后开始记录答案 if i >= k - 1: res.append(nums[q[0]]) return res

关键点是队列里存的是下标而不是值。为什么?因为你要判断过期下标是不是已经滑出窗口,只存值做不到这一点。这道题我一度以为自己写得很熟练,结果有一次用了值存储,加了个 while 循环去判断“当前最大值是否属于窗口”,代码变得极其难看,还出现了重复元素时误弹的问题。后来改成下标存储瞬间清爽。

5.3 热门 100 刷题顺序的小建议

既然这一章题目都来自热门 100,我顺便说下我对“热门 100 怎么刷”的看法。很多人拿到 Top 100 之后从头到尾怼,这样做效率很低,因为题号就代表难度节奏,并不是最优的学习顺序。我建议按照“题型”拆解:

  • 先把 Hash 相关的题清掉,因为它们是最好拿的送分题;
  • 接着做数组、双指针、滑动窗口,因为这类题套路明确;
  • 然后做二叉树和 BFS/DFS,这是面试出现频率最高的一块;
  • 再做动态规划,因为 DP 需要专门的时间啃;
  • 最后挑战贪心、图论、字符串难题。

接雨水和滑动窗口最大值正好处于双指针、队列、单调栈的交叉地带,属于必须吃透的中等偏上题。如果你能把这两题背后的思维迁移到其他题上,那 Top 100 后半程的很多所谓“困难题”其实也就是中等题的组合。

6. 周赛 430:现场编译与赛后复盘

6.1 四道题的考场时间分配

周赛 430 我这周也抽空打了一场。赛题记忆也许会褪色,但那种考场节奏感很值得写下来。根据个人经验,我把四道题分成这样几类:

题目预估难度目标用时核心考察点
第一题Easy5 分钟内字符串/数组模拟,边界条件
第二题Medium15 分钟内排序 + 贪心,部分前缀思想
第三题Medium20 分钟前缀和、计数、双指针
第四题Hard剩余时间状态设计或复杂结构

我的实际安排是:第一题绝不纠结,直接写最暴力的逻辑,前缀越界用哨兵处理;第二题读完题先想贪心策略是否正确,再动手,否则退回去看数据范围;第三题一般是最容易卡人的中等题,因为它在普通模板上加了额外条件;第四题如果 15 分钟内没有明确思路,我会先写个暴力解保底,至少拿部分分。

这种“先打底分,再抢高分”的策略,在周赛场景下非常实用。真打周赛的人都知道,不是每道题都要 AC,保证前两题零失误、第三题拿下、第四题尽力,就已经超过绝大多数参与者了。

6.2 容易在周赛里翻车的细节

周赛和每日一题最大的区别是:没有题解给你提示,时间压力还会放大你的低级失误。结合本周周赛,我复盘出几个常见的翻车点:

第一,读题时忽略数据范围。比如看到n <= 10^5还写了 O(n²) 的暴力,直接 TLE。读题后应该立刻反应出时间和空间复杂度的量级,这已经是刻进脑子的习惯。第二,排序题里把比较器写反,或者排序后没有考虑相等元素的顺序。第三,三题里用了前缀和但没有考虑下标偏移,差一位就是 WA。第四,比赛最后一两分钟还在改代码,结果把原本正确的版本改挂了。我现在的原则是:如果比赛还剩下不到 10 分钟且当前代码已经通过样例,那我宁可只加断言、做压力检查,也不做大范围重构。

6.3 赛后如何把一场周赛“吃干榨净”

每次周赛结束后的 1 小时,是学习效率最高的“黄金时间”。我会做三件事:先把所有 AC 的代码按“最优解”重新写一遍,因为周赛里你可能用了超长模拟,赛后要对比讨论区更优雅的解法;再把没 AC 的题翻题解,但不是看完就关,而是先尝试凭题解里的思路自己写 10 分钟;最后把这场的四道题添加到个人题解库的“周赛”分类下,并标注题型和错误原因。这样每次回看都很清晰。

我见到很多人打完周赛就彻底放下,隔两周回头看同样的题还是不会。这才是周赛最大的浪费。周赛是很好的“压力测试仪”,它能暴露出你知识体系里的薄弱环节,如果不能把这些反馈闭环到训练中去,那周赛就只是打了一个寂寞。

7. 本周刷题避坑手册(实用速查)

这一周我至少踩了十几个细节坑,很多都是可以提前预防的。我把它们整理成一张速查表,方便你贴在手边:

场景典型错误正确姿势
腐烂的橘子忘记特判fresh == 0BFS 前先看有没有新鲜橘子,没有直接返回 0
腐烂的橘子在 BFS 内重新扫描全网格维护fresh计数器,感染时减一
基本计算器括号内符号被外层符号漏乘弹栈时用栈顶的 sign 乘以括号结果
基本计算器空格导致字符判断失效i自增时跳过空格,不单独处理空逻辑
狒狒爱吃香蕉二分左边界取 0速度最小是 1,避免除零
狒狒爱吃香蕉hours <= h写成< h相等可行,边界条件一定仔细读题
接雨水用前缀/后缀数组时下标越界首尾单独赋值,或直接用双指针
滑动窗口最大队列里存值不存下标必须存下标,才能判断窗口过期
滑动窗口最大重复值被误弹用<=弹出旧的,新值保留

这些坑单独看都很小,但组合在一起,足以让你每一题都多调 10 分钟。我刷题时有一种感觉:很多时候不是思路不对,而是细节挡住了答题节奏。把这些坑提前记牢,解题速度会有肉眼可见的提升。

除了表格里的硬错误,我还想强调一个心态问题。本周的题全是经典题,导致评论区有很多人在“炫最优解”。你不必因为别人说“双指针秒了”就觉得自己笨。先能写出正确解法,再逐步优化,这个顺序永远是首要的。我自己回头看三年前的解法,也常常被自己当时的丑陋代码逗笑,但没有那段积累,就不会有今天的清晰思路。

8. 刷题指南:如何长期坚持每日一题不烂尾

8.1 每日一题的标准流程

在坚持每日一题这件事上,选择的方法比意志力更重要。我最推荐的标准流程是“20 分钟挑战法”:拿到题目先自己思考 20 分钟,如果思路清晰就直接写;思路不清晰就去看提示或题解的开头部分,看完后合上题解,尝试独立写完。

20 分钟后还是不会怎么办?直接看题解,但看题解也有讲究。不要只看最优解的最终代码,而要看它的“思考路径”。很多人习惯于看完整题解然后照抄,这样刷一百题都很难有本质提升。我会在看题解后做一个强制动作:把题解页面关掉,用自己的话说一遍思路,再重新写代码。如果写不出来,说明没真懂,得再看一遍。

如果某题你已经是会做的状态,那也不要跳过,试着换一种解法,或者想想“这题和之前哪题相似”。比如你已经会了接雨水的双指针,再想想单调栈怎么做;已经会了腐烂橘子的 BFS,再想想如何打印每一轮感染的橘子节点。这种“同一道题多种解法”的训练,对提升代码灵活性帮助很大。

8.2 建立个人题解库

我强烈建议你在本地维护一个 Markdown 格式的题解库,或者找一个支持标签功能的笔记工具。每一个题解记录下面至少要有四块内容:题意摘要、自己的思路演进、最终代码、复盘备注。其中复盘备注写这次踩了什么坑,以及下次再看时应该重点注意什么。

这个题解库最大的作用是“记忆外置”。人的记忆是不可靠的,过了两周你还能记得接雨水的双指针为什么左指针比右指针矮时结算吗?大概率不记得了。但如果你当初写了一句“矮的一侧决定了当前格子的上界”,再回来翻只需要 10 秒就能激活记忆。我现在的题解库已经积累了数百条,面试前翻一遍自己的笔记,比重新刷一遍线上题库快得多。

8.3 时间管理:不是每天都有 2 小时

我知道很多人坚持不了每日一题,不是因为懒,而是因为每天抽不出完整时间。我的建议是把任务拆成两档:工作日只保证“先做出最优解或搞懂一道题”,周末再集中补题和重写题解。你不需要每天留出 2 小时,哪怕只有 30 分钟,也足够做出一道简单题或复盘一道中等题。

另一个高效做法是“固定时间刷题”。我习惯把每日一题安排在早上,因为它能激活大脑;有人喜欢晚上刷,认为那时思路更清醒。关键不在于哪个时间好,而在于固定。到了某个时间点你就自动打开 LeetCode,形成惯性后,坚持的成本会急剧下降。

8.4 哪些题值得二刷

不是所有题都值得二刷。一道题是否值得进“二刷清单”,我会看三个标准:这道题是否用了超过两种算法思想?是否存在我第一遍没想通的边界?是否属于热门 100 或高频企业真题?本周的几道题里,我会毫不犹豫地把基本计算器和接雨水放进二刷清单,因为前者细节太密,后者有多种解法值得反复练习。

二刷不是把答案背下来,而是模拟“今天的我看到这道题,能不能第一眼就判断出题型,并写出优雅代码”。如果你能做到这一点,说明这道题已经被你消化了,可以把它从二刷清单移到“已掌握”分类。这个过程像滚雪球,你的已掌握列表越来越长,面试信心也会随之上升。

9. 关于这一周,我的个人体会

这七天刷下来,最大收获不是记住了某几个模板,而是重新体会到了“经典题为什么经典”。腐烂的橘子教会我多源同步扩散怎么建模,基本计算器教会我括号和符号的本质是上下文切换,狒狒吃香蕉让我再一次确认二分答案的万能适用,接雨水和滑动窗口最大值则是我反复拿出来对比双指针和单调队列的样本。这些题确实被无数人刷过,但每刷一次,我都能在细节上看到曾经忽略的东西。

最后分享一个小技巧:不要只在 LeetCode 网页上点“通过”就结束。我会在通过之后,再把核心代码复制到自己的题解库,并写一段“如果我是面试官,我会追问什么”。比如腐烂的橘子,面试官可能会问“如果烂橘子的扩散速度不是每分钟一格怎么办”;基本计算器可能会问“如果增加乘法怎么办”。想清楚这些追问,你的学习深度就不会停留在“AC 了”这个层面。

希望这篇复盘对正在刷题的你有点帮助。后面我再遇到好的周,会继续把“每日一题 + 周赛”展开写成总结,咱们实战里继续见。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询