ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LeetCode Hot 100刷题攻略:课程表式学习路径构建算法知识体系

LeetCode Hot 100刷题攻略:课程表式学习路径构建算法知识体系

把 LeetCode Hot 100 刷出系统性:我的课程表式刷题方案

先说说我为什么想聊这个话题。前阵子有位刚转行的朋友问我,说自己也刷了一个多月的 LeetCode 热题 100,每天两三道,可一到面试,碰到稍微变形的题还是发怵,总感觉脑子里只有零散的题号,没有一棵完整的知识树。这个问题我特别有共鸣——Hot 100 这个题单本身没有问题,它很经典,覆盖了面试里最常出现的高频考点,但它本质上只是一张"题目清单",不是一套"学习路径"。清单告诉我们刷什么,没告诉我们按什么顺序刷、每道题该配哪些前置知识、刷完之后怎么巩固。于是我自己重新做了一遍编排,把 Hot 100 变成了一个"课程表":按专题分块、按难度递进、按天排期、按复习节点回滚。这篇文章就把这套方案完整拆给你看,适合刚开始刷题准备面试的同学,也适合已经刷过一轮但觉得体系混乱、想重新梳理的人。

整个方案的核心就一句话:不要让题单成为你的目录,要让知识体系成为你的目录,题单只是题库。下面我按自己当时排课表的思路,一步步解释这套东西怎么搭起来。

1. 为什么"按题号顺序刷"是效率最低的刷法

如果直接打开 Hot 100 的题单,从第 1 题刷到第 100 题,会立刻遇到一个问题:知识点跳来跳去。今天的题考哈希表,明天的题考动态规划,后天的题又变回溯。我们的脑子在学习新东西时,最吃力的阶段恰恰是"建立神经回路"的初期,而建立回路最需要的是重复和相似性。知识点每天切换,昨天刚建立的哈希表思路,还没热透,今天就换成二叉树的遍历了,间隔时间一长,之前的印象差不多就凉了。

我当时第一次刷就是这样踩的坑。刷了大概二十来道的时候,回头看前面做过的题,很多竟然要想好一阵子才能想起来用了什么算法,更别提自己独立写出来了。问题不在于我不认真,而在于缺乏聚合性。人类记忆的规律摆在那里:同一主题的东西在短时间内集中反复出现,印象才深。这就像学英语,如果每天背的单词分布在各个字母段且彼此毫无关联,效率一定远低于按主题场景去记。

所以我的核心思路很明确:先把 Hot 100 里全部题目"拆碎",按底层数据结构和算法范式重新归类,然后按专题刷。同一个专题内的题目连续出现,你会发现很多题之间的解法高度相似,真正做到"做一题会一类"。

具体拆归类的时候,我按九个大类来分:

  • 哈希表与数组技巧类
  • 链表类操作
  • 双指针与滑动窗口
  • 二叉树与递归
  • 图与拓扑排序
  • 回溯与 DFS
  • 动态规划(含背包、序列 DP、路径 DP)
  • 贪心算法
  • 堆与优先队列、栈与单调栈

这九个大类基本覆盖了 Hot 100 的绝大多数题目。那少量跨专题的题怎么办?比如有些题既可以归入滑动窗口又可以归入哈希表,我给自己定了一条规则:归类时看这道题最核心的解题思想是什么,而不是看表面数据结构的名字。

举个例子,有一类在字符串里找无重复字符最长子串的题,表面上是字符串处理,有人会把它归进哈希表类,因为它确实用了哈希集合。但它的核心思想其实是"滑动窗口的右边界扩张和左边界收缩",所以应该归入双指针与滑动窗口类。这样的归类方式,才能保证你在同一个专题里不断看到"同一种思想"的不同变体,进而把思想本身练熟。

按这个逻辑做完归类之后,第二步是给每个专题内部排一个"难度阶梯"。Hot 100 的题目本身没有严格标注难度吗?其实有难度标签,但我的排法不是直接按 easy、medium、hard 排——同一难度的题,它的前置依赖也可能完全不同。我在每个专题内部,按"前置知识需求"来排序。比如动态规划类,先做一维 DP 的题,再做二维 DP 的题,再做状态压缩 DP 的题;二叉树类,先做遍历类题,再做修改树结构的题,再做构造类的题。这样每个专题内部就形成了清晰的知识递进,而不是难度递进。

做好了这两步,一张个性化的 Hot 100"课程表"才算真正有了骨架。

2. 课程表总览:把 100 道题排成九周计划

排课表不是把题平均分到每天里就完了,还得考虑两个约束:一是每天的学习负荷不能太重,二是每周的专题之间要有合理的衔接顺序。我当时给自己定的节奏是每天 2 到 3 道新题,每周完成一到两个专题,剩下一天专门做复习和重刷,这样一张九周左右的课表就成型了。

先说专题之间的顺序,这个顺序比很多人想象的更重要。我当时定的顺序是:哈希表与数组技巧 → 链表操作 → 双指针与滑动窗口 → 二叉树与递归 → 回溯与 DFS → 图与拓扑排序 → 动态规划(第一周基础)→ 贪心算法 → 堆栈与单调栈 → 动态规划(第二周进阶)。这样排的原因有三个:

第一,前置知识要先搭好。哈希表结构简单,数组操作直观,适合作为热身;链表涉及指针操作,锻炼的是对"引用"和"指针"的敏感度,这种能力在后续树和图里会反复用到。第二,把难度从低到高平滑过渡。树和递归本身就是大量题目的基础,尤其是 DFS 类题目,先有树的递归经验,再做回溯和图的搜索,会觉得自然地多。第三,把动态规划拆成两段。为什么不一次性把所有 DP 题刷完?因为 DP 是很多人的老大难,连续两周高强度搞 DP 会严重挫伤信心。

这里我把整个课表的专题分配整理成了个表格,方便你对照:

周次专题题量核心知识点
第 1 周哈希表与数组技巧约 14 道两数之和、去重、原地操作、模拟遍历
第 2 周链表操作约 10 道哨兵节点、快慢指针、反转、合并、环检测
第 3 周双指针与滑动窗口约 12 道左右指针、窗口扩张与收缩、维护窗口状态
第 4 周二叉树与递归约 14 道前中后序遍历、层序、最近公共祖先、路径和
第 5 周回溯与 DFS约 10 道排列组合子集、棋盘类、剪枝、状态恢复
第 6 周图与拓扑排序约 6 道邻接表建图、入度、BFS/DFS、环检测
第 7 周动态规划基础约 10 道一维 DP、二维 DP、编辑距离、最长子序列
第 8 周贪心算法与区间问题约 8 道排序后贪心、区间合并、跳跃游戏类
第 9 周堆、栈与单调栈约 8 道大顶堆小顶堆、单调栈、条件栈
第 10 周动态规划进阶约 8 道背包问题、状态定义优化、子序列综合

课表的总题量比我列的高出了一些,这是因为部分题目在多个专题中都有练习价值,我允许自己"跨专题二刷",每道题反复做几次,计入多个周次的练习量。等你真的排课时你会发现,Hot 100 的题目之间交叉关联很多,这是好现象,说明知识网络正在形成。

你可能会问,既然按专题刷,那和按题号的顺序刷相比,最大的体感差别是什么?我在按这个课程表执行到第二周时,一个特别明显的感觉是:很多题的代码结构开始变得像一个模子刻出来的。比如链表题,一旦你吃透了"哨兵节点 + 三指针"那套模式,反转链表、合并链表、重排链表这些题,核心框架都是一样的,差别只是细节。

这种"触类旁通"的体验,是孤立刷题很难获得的。

3. 每天的执行细节:一道题怎么才算"刷过"

课表只是宏观规划,真正决定效果的是每天的微观执行。我在执行过程中逐渐确定了一套"一道题四遍法",每一遍的侧重点都不同,这里详细拆解一下。

第一遍是独立思考与暴力尝试。拿到一道题,我给自己限制 20 到 30 分钟,不查任何资料,先自己分析。这个阶段的目标不是做出最优解,而是形成自己对这个题的理解,包括:题目输入输出是什么?暴力解法是什么?暴力解的时间复杂度能不能接受?如果不能,瓶颈在哪儿?如果完全没有思路,那也要在纸上写一写自己尝试过的几个方向,哪怕只是画几个例子也行。这一步是最痛苦的,但它决定了你对这道题的记忆深度。很多人在这一步草草放弃,直接看题解,结果就是"看懂了"但永远"不会写"。

第二遍是查阅题解,理解最优解并梳理思维链。这里有一个关键动作:不要只复制代码,而是要把题解的思路用自然语言写一遍。比如"这题是滑动窗口,窗口左边界收缩的条件是 count 大于 k"——你要把这个条件写出来,同时搞清楚两个问题:为什么这个条件成立?这个条件是怎么从暴力解中演化出来的?把这两条理解透,才算真正掌握了这道题。

第三遍是独立重写,不看题解。通常我会隔几个小时,甚至第二天再来重写这道题。写完之后再对照题解看自己哪里写得不够简洁,哪里变了形。变形不可怕,可怕的是不知道变形的原因。如果你重写的时候能顺手写出解题的关键判断条件,说明你的思路基本通了。

第四遍是周度回滚。每周抽半天时间,不看笔记,把本周做过题中"当时觉得比较难"的几道重新做一遍。如果还是做不出来,说明那周的专题吸收有问题,需要退回重来。这个回滚机制是整个课程表里最有价值的部分,它把"学了就忘"从一种情绪变成了一个可以检测和修复的问题。

在执行这套方法时,我对笔记格式也做了规范。每道题的笔记只需要四行:

  • 一句话思路,例如"双指针,遇 0 移到末尾"
  • 关键边界条件,例如"左指针越过右指针时停止"
  • 时间/空间复杂度
  • 自己的易错点,例如"倒数第二个节点容易丢"

这种极简笔记最大的作用,是在复习时快速召回思路,而不是让你浪费时间重新读整段代码。

4. 几种典型专题的执行模板:以滑动窗口、动态规划、回溯为例

有些专题光靠常规刷法不够,得针对它们的内部规律设计专门的"刷题模板"。这里我挑三个最典型的专题展开说,因为这三个容易让人死磕。

4.1 滑动窗口类:先把"窗口语义"想清楚

滑动窗口题在 Hot 100 里占比不小,核心其实是一个窗口扩张和收缩的循环框架。我在刷这一类题时,每次会先在注释里写出三件事:窗口里维护的到底是什么信息、右指针扩张时信息如何更新、左指针收缩的条件是什么。

举个例子,求子数组最大均值这种题,窗口长度固定,只要算前缀和就好;而求无重复字符最长子串这类题,窗口长度不固定,逻辑就变成了"先扩张再收缩"的循环。很多初学者觉得滑动窗口难,是因为没理解"窗口里的信息"和"收缩条件"是绑定的:你维护的是一个计数数组,那收缩条件就是"某个字符计数超过 1";你维护的是区间和,那收缩条件就是"区间和大于目标值"。

我发现,把这套"语义三问"提前写下来,能大幅降低题目间的迁移成本。每道滑动窗口题,本质上都是在给这三个问题填不同答案。

4.2 动态规划类:先定状态,再谈转移

动态规划是传说中"看懂了、不会做"的重灾区,我自己的经验是:不要上来就想状态转移方程,先逼自己回答两个问题——dp 数组的每个格子代表什么含义?最终答案对应 dp 的哪个位置?这两个问题答清楚了,转移方程其实是在描述"由一个已知格子推导到另一个未知格子的操作",它应该顺理成章地浮现出来,而不是靠灵光一现。

举个例子,最长递增子序列这类题,状态定义是"以第 i 个元素结尾的最长递增子序列长度",那么转移方程就是"遍历 i 前面的所有元素,如果前面的元素值小于当前值,就用前面的状态加 1 来尝试更新当前状态"。这样理解之后,你会发现 DP 题的关键不在转移方程的形式,而在状态定义是否选对了。状态定义对了,方程是自然的结果;状态定义错了,方程再复杂也是空中楼阁。

我还固定在每周六做一次"DP 状态定义默写",把本周做过的 DP 题的状态定义、初始值、答案位置分别写在一张纸上,不看代码。这种默写能检验你到底是理解了 DP 的骨架,还是只是背会了代码。

4.3 回溯类:画出决策树再写代码

回溯题的本质是暴力搜索加剪枝,而很多人直接上手写递归,写一半就乱了。我后来找到一个稳定的套路:做题前先在草稿纸上画出"决策树"——每个节点表示当前选了哪些元素,每个分支表示下一个元素选谁。画出树以后,代码基本就是一次 DFS 遍历:递归函数里有一个 for 循环尝试所有分支,循环体里先做选择,然后递归,最后撤销选择。

这个"选择-递归-撤销选择"的节奏,是回溯题通用的骨架。剪枝本质上是在 for 循环里加一个 if 判断,提前砍掉不可能产生解的分支。我特别想提醒的是,撤销选择这步非常容易漏。漏掉撤销选择,会导致排列结果互相污染,输出一堆奇怪的重复序列。刷回溯题时,只要发现输出有多余分支,第一时间就该怀疑撤销操作的缺失。

5. 课表执行中的常见翻车现场与应对方案

按课表刷题,跟任何计划一样,一定会遇到执行层面的意外。我把自己走过弯路总结成了几类高频翻车现场,并给出了对应的止损办法。

第一类翻车是专题内难度评估失真。我按"前置知识需求"排的难度阶梯,偶尔会和真实体感冲突,比如一道归为"简单"的题,因为题目描述太长,读题就花了十分钟。应对方案是:允许自己把某些题调出当前专题,推到下一周。课表不是法律,它只是一个优先级排序。如果一个专题里出现了三道连续卡壳的题,说明这个专题的内部顺序需要调整,而不是你的能力出了问题。

第二类翻车是复习回滚被挤占。计划里的周度复习时间,经常被加班或者临时事项挤掉。我发现对抗这个问题的办法是"把复习当成今日的一道新题"来看待:也就是在课表里把复习任务本身排进每天的新题配额中。比如今天计划做两道新题加一道复习题,输入到表格里,这样复习就不会被"优先级更低"这种心理暗示给挤掉了。

第三类翻车是同类题目做太多产生倦怠。按专题刷的最大副作用就是连续几天都在同一个模式里转圈,容易腻。我自己的处理办法是:每完成一个小专题(比如链表内部的合并类),就奖励自己挑一道其他专题的题换换口味。这样既不打乱大节奏,又能调节情绪。

第四个特别值得说的是只看题解不写代码。很多人刷题会进入"眼睛会了,手不会"的状态——题解看懂了,思路理顺了,但就是不动手。归根结底是潜意识里把"刷完题数"当成了目标。我后来给自己立了个规矩:每道题不管多简单,都必须亲手提交一遍并通过全部的测试用例。多简单的题都一样,因为手写的过程才是真正建立连接的过程。

6. 把课表进一步变成"复习索引":面试前一周怎么用

课程表的价值不只在于前九周的执行,它还能在面试前的一周内发挥巨大作用——变成一张快速复习索引。到了这一阶段,不用再按专题顺序从头刷,而是按课表里的知识树节点,从上到下快速过"笔记"。

我的做法是,把每个专题内的题目按"星标"和"笔记"重新整理一遍,然后按以下三步快速复习:第一步,每天过两个专题的笔记,每个专题大概 40 分钟;第二步,针对星标题,在纸上写出思路流程,从状态定义到转移方程,再到边界条件,不写完整代码,只写关键判断;第三步,挑几个高频高价值的硬核题,完整手写一遍代码,保持手感。这个阶段的目的不是学新东西,而是把已有的知识从"知道"变成"瞬间可提取"。

我在这个阶段还有一个小习惯:用课表的专题名作为口述提纲,假装自己在给面试官讲题。比如讲"滑动窗口",我会说:"滑动窗口的关键是维护窗口内信息,右指针扩张,左指针在条件不成立时收缩,复杂度通常是 O(n)。"这种口述练习对面试的帮助巨大,因为面试官很多时候不只看你会不会做,还看你能不能讲清楚思路。能被你讲清楚的题目,才是真正属于你的题目。

7. 一些常见的争议与我的看法

关于 Hot 100 课程表这件事,有几个经常被拿出来讨论的问题,我也说说自己的答案。

问题一:按专题刷会不会导致题目综合应变能力差?—— 有一定道理。所以我在整个课表里特意安排了"跨专题二刷"环节,也就是在原计划接近尾声时,不再按专题划分,而是随机挑组合题来练。到那时你已经对每个专题的核心范式有了肌肉记忆,跨专题练习就是为了把这些范式组合起来使用。

问题二:课程表适合基础非常薄弱的人吗?—— 适合,但建议在前面加两周的预备期。预备期不做 Hot 100,而是先简单过一遍基础数据结构操作:数组遍历、链表插入删除、栈与队列的 API、二叉树的前中后序遍历。有了这些地基,课程表里的 100 道题学起来会顺畅得多。

问题三:要不要死磕一道难题?—— 我的经验是:独立思考 30 分钟内没有任何有效进展,就果断看题解,把题解思路弄懂后,第二天再独立重写一遍。难题的收获在于"看懂思路"和"复现思路"这个过程,而不在于"苦思八小时"的感动自己。

问题四:课表计划执行到一半不想继续了怎么办?—— 这是特别普遍的状态,别自责。我做过的决定是暂时停下新题,用两三天只做已经做过的题目里最简单的几道,找回掌控感和节奏感,再回来推新课表。无论如何不要彻底放下,间隔时间一旦超过一周,之前建立的知识网络会明显淡化,再启动的成本会高很多。

整体上,我强烈建议每个人动手排一份自己的课程表。你可以完全借鉴我上面的专题分类、周次安排和执行方法,也可以根据自己的薄弱项调整顺序和节奏,比如你链表已经很熟,完全可以压缩到半天;你 DP 特别弱,可以给它额外的两周。关键是把"题单"升级成"课程表",让每一次做题都在有逻辑地构建自己的算法知识体系。

最后再分享一个我特别受益的小技巧:在每个专题结束时,写一句"专题总结"存进笔记开头,记录这个专题里最核心的思考方式是什么,以及你自己最容易犯的错是哪种。这份总结在复习阶段比任何题解都管用,因为它是从你自己的错误里长出来的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进