刷了三百道题,面试时还是被一道“简单题”问住了,那瞬间脑子一片空白,事后看题解发现思路自己明明见过。这种经历你有过吗?如果有,问题大概率不在刷题量,而在算法题复盘没做到位。后来我把复盘这件事单独拎出来,当成和刷题同等重要的一项工作,情况才真正开始转变。这篇内容不聊玄学,就讲讲我现在用的一套算法题复盘方法——为什么复盘能比刷题更有价值、从哪些维度拆题、怎么让复盘结论在面试里真正用得上,全是实操过的经验。
1. 算法题复盘到底在“复”什么
1.1 不要把“背下题解”当成复盘
我见过很多人刷题之后打开题解,看两遍觉得“哦,懂了”,然后关掉页面,觉得自己已经搞定了这道题。下次再碰到类似题目,最多记得“好像可以用双指针”,但双指针为什么会快、什么条件下能用、边界怎么处理,全忘光了。这种状态等于没做算法题复盘,只是进行了一次浅层浏览。
复盘的“复”,不是把题解再读一遍,而是把这道题的完整思考过程重新走一遍:我第一次见到它时卡在哪一步?为什么卡住?正确的解法是怎么突破这个卡点的?这个解法背后对应哪种通用思想?下次遇到类似的结构我该怎么快速识别?只有把这些链条理顺了,这道题才算真正内化成自己的东西。
还有一个容易踩的坑是把正确解法背得滚瓜烂熟,但完全没想过“为什么是这个解法”。我曾经在一个模拟面试里碰到一道数组找峰值的题,对方上来就写二分,结果被问“为什么可以二分”时愣住了。很多题解不会写清楚“为什么”,但恰恰是这些“为什么”决定了一个算法能不能在别的题目中迁移。
1.2 复盘的核心维度:知识点、思路、实现、复杂度
我一般从四个维度拆一道题,每次复盘都按这个框架走一遍。
知识点维度,解决的是“这道题在考什么”。是哈希表、排序、还是动态规划?如果是动态规划,属于哪种状态设计模式?知识点本身没有对错,但它决定了你要往哪个方向去找思路。比如看到“求连续子数组的最优解”,大概率要想到滑动窗口、前缀和或动态规划,而不是去纠结链表。
思路维度是最花时间的。我复盘时会把自己第一次的思考和标准思路放在一起对比,写出自己卡住的那个点。比如没想到用哈希表去记录出现过的位置,或者没意识到可以先把数组排序再来处理。这个“卡住的点”才是复盘最重要的资产,它明确告诉了你思维习惯里的短板。
实现维度是很多人忽略的。明明思路对了,代码却写得很乱,边界条件漏了一堆。更典型的是写了很多辅助变量,逻辑绕来绕去,最后连自己都看不懂。这种情况下我会复盘代码结构:能不能拆出更清晰的循环不变量?尾边界怎么处理更自然?有没有更简洁的表达方式?
复杂度维度包括时间和空间。我会问自己:这个剪枝到底省了多少时间?有没有所谓的“以空间换时间”?最优解是否还能优化?把复杂度分析做透,才能真正理解为什么某种思路在一些极端测试下会挂,面试官问到“还能不能优化”时才能接得住。
1.3 先定标准:一道题做到什么程度才叫“过”
算法题复盘如果没有验收标准,很容易变成无效努力。我给自己定了两条线。
第一条线叫“闭卷重做”:合上题解和笔记,独立把代码写出来,不能有一步卡壳。能抄着写出来不算会做,能默写出来才算初步掌握。第二条线叫“能讲给别人听”:用口语把这道题的思路讲清楚,包括为什么用这个算法、边界怎么处理的、复杂度多少。讲得明白,说明你是真的理解了,而不是只记住了代码。
注意:这两条线一定要分开来看。有些题我默写代码没问题,但一讲思路就语无伦次,这就是“知其然不知其所以然”,是算法题复盘没做到位最典型的表现。
2. 我常用的算法题复盘框架与记录模板
2.1 三层复盘节奏:当天、隔天、一周后
一套成熟的算法题复盘不能只做一次。我现在用的是三层节奏,对应不同的记忆阶段和认知阶段。
第一层是“当天复盘”,在完成题目后的当天内做。这个时间点距离刚刚的刷题过程最近,细节还热着,趁热把卡点、错因、题解思路记下来。重点记录那些让自己“啊”一声恍然大悟的瞬间,这些是思维升级的关键线索。
第二层是“隔天复盘”,隔一天之后重做一遍。隔天是个很神奇的时间点,既不会像做完马上就能背出来那样虚假熟练,又不会因为隔太久而完全忘了。这时如果还能顺利写出来,说明初步记忆已经形成;如果卡住,说明前一天的“懂了”很可能是假的。此时我会重新读一遍当天的笔记,标记出第二遍依然卡壳的地方,这些地方才是真正的薄弱点。
第三层是“一周后复盘”,一般放在周末进行。我会把本周所有标记过卡点的题全部翻出来,不看任何提示,白板或文档里重写一遍。这一轮能过的题,基本可以进入“已掌握”清单;仍然卡住的题,我会把卡点再补充进记录,并决定是否需要再做一次专项训练。
这三层节奏执行下来,最大的感受是:大部分“刷过但好像没学过”的题,都是因为只做了第一层,后面两层直接跳过了。而恰恰是那个“隔着一夜再写一遍”的动作,才真正让记忆从短期转入了长期。
2.2 可复用的复盘记录模板
很多人不肯复盘,是因为觉得“太费时间,不知道记什么”。我把复盘记录固定成一个模板,每次就往固定字段里填东西,速度很快,信息密度也高。
我常用的字段是:日期、题号/题目名、难度、知识点标签、我的第一思路、卡住的环节、标准思路关键词、代码实现要点、复杂度结论、下次需要重点复习的点。这个模板用表格或者列表都能存,我习惯用一个本地知识库来维护,字段就是这些。
举一个典型的填充例子。某道“最长无重复子串”的题目,我会这样记:
- 知识点标签:滑动窗口、哈希表
- 我的第一思路:暴力枚举所有子串检查是否重复,但感觉复杂太高,没继续想
- 卡住的环节:没想清楚滑动窗口怎么扩展和收缩
- 标准思路关键词:用哈希表记录每个字符最后一次出现的索引,窗口右移时更新左边界
- 实现要点:左边界取 max(当前左边界, 上次出现位置+1),注意索引从0开始
- 复杂度:时间 O(n),空间 O(字符集大小)
- 下次重点复习:滑动窗口类题目,左手边界的收缩条件
这样一张记录,既有“我错在哪”,又有“正确解法怎么来”,还有“下次该提醒自己什么”。比单纯存一份题解截图有用得多。
2.3 优先级金字塔:不是每道题都值得深度复盘
用这套模板之前,我还有过一个误区:每道题都做着同等深度的记录,结果有些题目花了很多时间,但价值很低,反而把精力从真正重要的题上抽走了。
后来我把刷题按“复盘点值”分成了三层。顶层是“高频考点型题目”,比如二分、双指针、BFS/DFS、动态规划中的常见模式,这些题必须做完整四维复盘,且要做完隔天、一周后的两层复查。中层是“中等难度核心题”,它们可能场景很具体,但涉及的知识点能迁移到其他题目,这类题做当天和隔天复盘就够。底层是“偏题怪题”或者你已经完全掌握的题,这类直接过,顶多在笔记里记一个“这道题用了某某思想”的标签,不做深度展开。
优先级判断的一个实用标准是:这道题如果不做,面试时碰到同类题我会不会心虚?会,就按最高优先级处理。这套金字塔把复盘的投入产出比拉高了很多,精力有限的情况下,把好钢用在刀刃上比什么都重要。
3. 用一道题完整演示算法题复盘操作
3.1 一道“两数之和”的完整复盘过程
“两数之和”大概是很多人刷题的第一关,但有意思的是,不少刷题一年多的人在这道题上依然会被问出破绽。我用它来做算法题复盘演示,因为它足够简单,能把流程讲清楚;同时它也足够经典,涵盖了暴力、哈希、排序双指针三种思路。
我第一次做这道题时,第一反应很自然——两层循环嵌套,遍历所有组合,找到一组就返回。当时觉得没问题,确实能通过测试,但面试官追问“时间复杂度多少”时,我看了两秒才说 O(n²),然后对面接了一句“能不能优化”,场面就有点冷了。
现在按我的复盘模板过一遍。知识点标签很清晰:哈希表、数组遍历。我的第一思路是暴力枚举,卡住的环节是“如何避免第三层查找的开销”。标准思路是:用一个哈希表存储“数值-索引”映射,在遍历每个元素时,直接查“目标值-当前值”在不在哈希表里,在就直接返回。
实现要点我复盘后会特别注意三点:一是如果目标是返回元素下标,哈希表里存的就是索引,不要存成值;二是边遍历边插入,这样不会误配同一个元素;三是如果题目要求返回元素本身而不是下标,可以直接用哈希集合而不是哈希表,省一层空间。
复杂度方面,暴力是 O(n²) 时间、O(1) 空间;哈希优化是 O(n) 时间、O(n) 空间——标准时空权衡。我还额外加一条:如果题目要求先排序,那就可以用双指针把时间压到 O(n),但代价是改变了数组的相对顺序,要返回原始下标就麻烦了。
3.2 从“看得懂”到“写得顺”
手法层面的复盘,我会用伪代码重新梳理一遍逻辑,把每一步循环前后“当前状态是什么”写清楚。比如这道题的标准思路,我会按这样的顺序复述:
建立空哈希表 map 遍历数组 nums,得到当前索引 i 和值 x 计算 target-x 如果 target-x 存在于 map: 返回 [map[target-x], i] 否则: 记录 map[x] = i 如果循环结束仍未返回,按题目要求给出结果把这段逻辑写成伪代码之后,我还会自己追问两句:为什么“边遍历边插入”是对的?因为如果先全部插入再遍历,可能同一个元素被用两次。为什么不需要特判“当前元素自己是不是答案”?因为查询发生在插入之前,当前元素还没进表,天然不会和自己匹配。这两个问题虽然基础,但非常能检验一个人是不是真的理解了哈希思路。
写代码时还有一个容易出现的问题:哈希表里存下标时,如果题目数据范围很大,要不要提前申请容量?在我的经验里,如果语言支持初始化容量,给一个预估大小能明显减少扩容开销;如果不支持或者偷懒不写,数据量小的时候影响也不大。复盘时会注意把“预分配哈希表”的写法掌握,因为笔试题数据一大,这种细节直接决定能不能跑完所有用例。
3.3 把复盘结论提炼成面试表达
这也是我做算法题复盘的一个习惯:每一次深度复盘,最后都要变成“一段能直接说给面试官听的话”。做完两数之和的上述复盘后,我会整理成大概这么一段表达:
“这个题有两个需要考虑的维度,一个是能否处理数组中存在重复元素的情况,另一个是把时间复杂度从 O(n²) 降到 O(n)。我会用一个哈希表,在遍历时记录每个值出现的下标,同时检查 target 与当前值的差是否已在表中,若在就直接返回。这样的哈希思路本质上是记录了历史信息,用空间换取了遍历过程的重查时间。”
这种表达在面试现场特别有用,因为面试官听到的不是“我知道这题能哈希”,而是“你不仅知道能哈希,还知道为什么哈希、代价是什么”。当你把复盘的每个细节都转化成这种表达,面试中遇到同一知识点的题目时,说出来的内容就会比光刷题的人厚实一截。
4. 算法题复盘中常见的坑与救场办法
4.1 复盘变成“抄答案工程”,越复越没感觉
很多人包括我一开始都干过:打开题解页面,把代码往文档里一粘贴,写上“最优解O(n)”几个字,就觉得完成了复盘。这种算法题复盘根本不是复盘,只是一份抄写作业。因为它完全跳过了“为什么”和“我怎么没想到”这两个最关键的部分。
避免这个坑的办法,我是用“三不写”来约束自己:不看题解不能手写实现的不写;说不出为什么用这个算法的不写;跑不出几个典型测试用例的不写。凡是触及这三条中任意一条,都要回到题目重新思考,直到能完成其中一项为止。尤其是“跑测试用例”这一步,我会把题目给的示例和一些边界情况(空数组、全相等、最大最小值等)在纸上跑一遍,看代码逻辑是否真的覆盖了所有分支。
如果发现自己确实想不出来,需要看题解,那我会强制追加一个步骤:把题解合上,用自己的话重新推导一遍完整思路,并且写一个“为什么我会卡在这里”的反思。这一步的意义在于,让大脑从被动接收切换成主动加工,而不是让眼睛和手指机械地走一遍流程。
4.2 只复难题,不复简单题
有段时间我总觉得简单题没什么好复盘的,几分钟一过就完了。但后来发现,真正在面试里手抖的,往往不是那些难得离谱的题,而是表面看起来很简单、细究起来全是细节的基础题。比如一个字符串翻转,能写出 N 种写法的人很多,但能在三分钟内写出没有边界问题的版本又是另一回事。
算法题复盘的覆盖面应该像一个漏斗,而不是一根只指向尖端的针。难题考察的是思路复杂度,简单题考察的是基础扎实度。两者都需要拆解、记录、总结。我现在的做法是:简单题复盘的重点不是“会不会做”,而是“能不能写得快、写得稳、边界全”;中等题复盘的重点是“思路从哪来”;难题复盘的重点是“模型的抽象与迁移”。这给了每道题一个明确的复盘目的,复习时也就不会觉得“简单题没必要”了。
4.3 只记结论,不分析复杂度与边界
这是很多刷题笔记的通病:在标签上写着“用滑动窗口”,在代码里写了一个 while 循环,然后就结束了。但等你过两周回看时,根本不知道自己当初为什么选择滑动窗口,也不知道时间复杂度的具体推演过程。
我在一次复盘“岛屿数量”类 BFS 题时,把“O(mn)”直接标在了答案上。一周后重做时我顺手问自己,这个 O(mn) 是怎么来的?结果发现我并没有特别清晰的推演:为什么每个格子最多入队一次?关键点是要说明白 visited 标记保证了不会重复入队。这个推演虽然简单,但它才是复杂度的本质。如果一个记录做不到这种程度的自洽,那这份记录就只能算一份索引,不能算复盘笔记。
边界的分析同样不能省。很多代码挂在边界上,常见的有:数组只有一个元素、目标值不在列表中、滑动窗口的右指针越界、字符串为空、链表为空或只有一个节点……我不可能把所有边界都穷举一遍,但至少会把题目描述的约束范围和输入类型里很可能会触发问题的几种情况跑一遍,并且记录运行结果。这个习惯帮我避免了很多“本地能跑,换了样例就挂”的尴尬。
4.4 不复盘“当时为什么会卡住”
我发现不少人的笔记里有“这是个XX题,解法是XXX”的结论,却没有记录自己摸索过程中的断点。其实复盘最重要的价值恰恰就在那个断点上——那是你自己的知识缺口,不是题目的常规解法。
我在做一道经典“接雨水”题时,第一遍死磕了很久,历时一个小时才勉强写出了暴力解,后来看题解发现可以用双指针,一个方向扫一遍就能算完。我当时觉得“双指针真是妙”,但我更该问的是:为什么我没想到双指针?后来复盘发现,是因为我对“双指针适用的前提”缺乏认知,只记得“有序数组可以用”,而忽略了“某些情况下可以通过维护左右状态来压缩遍历次数”。
看清这个底层原因后,后续碰到“最大面积”“盛最多水”这一类题,我都能更快地往双指针方向试了。“卡点”是算法题复盘中真正的宝藏,如果一个复盘笔记里没有“卡住在哪、因为什么、怎么突破”这三项内容,那它的价值就打了大折扣。
5. 让算法题复盘真正落地为硬实力的工具与习惯
5.1 用“费曼输出法”把复盘变成表达能力
我反复提到“能讲给别人听”,这在算法题复盘里不是可选项,而是必选项。费曼输出法落到算法题上,具体操作是:把一道题当成给一个完全不会的人讲课,顺着“题目要求什么、用什么算法、为什么用这个、边界怎么处理、复杂度多少”讲一遍。
实际操作中,我会定一个规矩:每做完一次深度复盘,至少要写一版“自己的题解”,内容不是把提交代码抄一遍,而是用自然语言+关键代码片段讲清楚思路。这种题解有没有公开并不重要,重要的是形成“用自己的语言重新组织解法”的习惯。写题解的过程中最容易暴露问题:哪些地方你以为自己懂,一开口发现没法自圆其说,那些地方就是还需要补课的薄弱点。
5.2 周期性串题:把零散题目连成知识网络
单题复盘做多了以后,笔记里会出现一堆孤立的点,这时候需要一个“串题”动作把点连成网。我一般每隔两周做一次。做法很简单:打开这轮所有题目的记录,按知识点标签分组,然后找出每组题目里的共性结构和差别。
比如标签为“双指针”的题目可能有五六道,有的是对撞双指针,有的是快慢双指针,有的是滑动窗口。我会把这几道题放到一起比较:什么场景用对撞、什么场景用快慢、什么场景用滑动窗口?再写出各自的模板或者伪代码。这个过程做完,双指针这一类题在我脑子里就不是散装的了,而是一条有主干、分支清晰的知识树。
周期性串题还有一个额外收益:它能帮你发现哪些知识点“看起来刷过很多题,其实全是同类题”。比如你标了十道“动态规划”题,结果细细一看,全是“一维线性递推”,而区间DP、树形DP、状态压缩DP根本没碰到几道。只有在串题时才会有这种感觉,单题复盘中很难发现。
5.3 面试模拟:把复盘笔记变成临场答案库
最实在的一步,是把算法题复盘笔记当作面试模拟的素材来使用。我做过一段时间的高频模拟面试练习,遇到一道题,我会先不看笔记,尝试用记忆中的“复盘中总结的表达”去讲,然后再打开笔记对照差在哪。这个环节会打磨出两样东西:一是表达的简洁度,二是对细节的熟练度。
还有一个很有用的做法:给每道深度复盘的题写一小段“一句话总结”。比如“看到需要在线查找/记忆之前访问过的信息,优先想哈希表”“面值组合类问题先想背包模型的可取状态”等等。这些一句话总结在面试前快速翻阅时价值极高,它们比长篇笔记更容易形成条件反射,帮你在有限时间内快速锁定思路。
最后分享一个我一直在用的小技巧
我给自己的算法题复盘定了一个硬规矩:每道深度复盘的题,必须留一笔“如果失忆了,希望未来的自己看到什么”。换句话说,就算这道题三个月后完全忘了实现细节,只要看到这条笔记,也能在五分钟内重新把思路推出来。这个要求听起来简单,实际上非常考验复盘质量,因为你必须筛掉一切不重要的装饰,只留下那些真正影响理解的关键链。
写算法题复盘不是写日记,它是给自己未来的判决书,越是认真对待每一次卡点和突破,越能在真正的面试和实战里得到回报。如果你现在刷题也陷入瓶颈,不妨把算法题复盘从“附带动作”升级成“核心动作”,坚持几周,你大概率会发现,思路通畅的程度和看的题解数量不成正比,但和你自己的复盘深度严格成正比。