有些题看答案时觉得“这不难啊”,隔几天再写,还是不知道从哪里下手。这组笔记想补的就是中间这一步:为什么想到这个方法,代码里的变量在记什么,换个条件以后还能不能用。

选题参考 Top Interview 150,主要讲 Easy / Medium 中经常能用到的方法,不按 150 道题逐题整理答案,Hard 也暂时不展开。这里的链接都是公开练习,不是公司原题合集。

1. 按方法走,不按题号走

刚开始刷题,可以从前两篇看起:先熟悉 Python 工具,再练“把见过的信息存起来”。已经刷过一些,就直接找自己卡住的题型。每篇先拿小例子把过程走清楚,代码和复杂度放在后面,需要时再往下看。

顺序 小节 主要讲什么
1 复杂度与 Python 工具 average / amortized、递归栈(recursion stack)、dict / deque / heapq 怎么选
2 Hash map 与前缀和 key 存什么、value 存什么,为什么有时要存频次
3 双指针、窗口与二分 每次移动,凭什么能排除一部分答案
4 DFS / BFS:递归还是迭代 访问顺序、栈与队列、visited、最短路的前提
5 栈、堆与链表 等一个未来答案、维护 Top K、安全地改指针
6 回溯与动态规划 路径和状态怎么区分,记忆化和循环怎么互换
7 排序、区间与贪心 排序省掉了什么,局部选择为什么不会后悔

第一遍不用把所有方法一起背。基础模板熟悉后,按自己卡住的问题往下读:

我卡在这里 接着看 这次多想一步
字符串匹配总是从头重来 字符串匹配 · KMP 失败后哪些已匹配信息还能复用
窗口最大值离开后不知道谁接班 单调队列 一个候选(candidate)为什么可以永久删除
BST 只会写递归(recursion)模板 二叉搜索树 祖先范围、中序排名与树高
DP 的循环方向老是反 背包与凑钱 读上一层,还是允许复用当前层
LCS、LIS 与子串搞混 序列 DP 前缀、结尾、连续,分别限制什么
只存一个最优值就丢了规则 状态机 DP 哪段历史会改变后续可选动作

还有一篇按需看的质数判断与筛法:一个数用试除,一段数用筛表,重点是平方根边界和预处理何时值得。它不在第一轮必刷清单里。

语法不熟,先看朋友整理的 Python 语法。这里更关心“这行代码在算法里起什么作用”,两篇可以来回查。

2. 读完题,先问 5 个问题

  1. 输入有什么保证? 已排序?可能有负数?有重复?图里有环吗?
  2. 输出是什么? 存在与否、一个答案、最优值,还是所有答案?
  3. 重复做的事是什么? 每次从头找一个数,还是每次重新算一段和?
  4. 要留下什么信息? 下标、频次、窗口、路径,还是某个状态的最优结果?
  5. 能扔掉什么? 为什么扔掉后不会漏解?这就是不变量(invariant)和正确性的起点。
题目里的信号 优先考虑 先确认的前提
“见过吗 / 几次 / 哪个位置” set / dict / Counter 要不要保留重复次数、原下标
连续子数组或子串 窗口 / 前缀和(prefix sum) 有负数时,和的单调性可能消失
有序,或“从这里开始都可行” 二分 能否写出单调判定
连通、层数、最少几步 DFS / BFS 普通 BFS 的边代价需相同
列出全部组合 回溯(backtracking) 输出本身可能指数级
很多路径通往相同子问题 DP / memoization 状态是否包含所有影响未来的信息
只关心最大的几个 小顶堆 是否真的需要全部排序

表里的提示只能帮你缩小范围,不能看到“最短”两个字就决定用 BFS。同样是在求最短,图上的步数和凑金额的硬币数,想法就不一样;还是得回到题目给的条件。

3. 一轮怎么复习

  • 先讲再写:用一两句话说暴力法,再说准备省掉哪部分重复工作。
  • 手走 4~6 个元素:把变量写下来,别只在脑子里觉得“应该对”。
  • 关掉答案再写一次:写完自己挑空输入、重复值、极端顺序试一试。
  • 隔几天换个条件:有序变无序、正数加负数、树变带环图。说说哪里会失效。

不用追求刷了多少题。隔几天还能写出来、能解释换个条件后哪里要改,就比记住题号更有用。

如果面试用英语,平时也留一点时间用英语讲。中文正文会在关键术语旁标出英文。看懂一个例子后,试着不看笔记,用英语讲清“状态存什么、下一步怎么走、为什么不会漏、复杂度是多少”。几篇里也放了简短的英文讲法,可以先对照,再换成自己的话;需要整篇英文时,点页面上的 English 就能切换。

4. 代码可以直接跑

核心模板在 patterns.py 和 deeper_patterns.py,用 Python 3.10+ 就能运行,不依赖第三方库。要连同下面的测试一起跑,请用 Python 3.11+:测试会读取 TOML 配置,用到 3.11 才加入标准库的 tomllib。在仓库根目录运行:

python interview/code/patterns.py
python -m unittest discover -s site/tests -p 'test_algorithm_patterns.py'
python interview/code/deeper_patterns.py
python -m unittest discover -s site/tests -p 'test_deeper_algorithm_patterns.py'

测试会拿小输入枚举答案,检查窗口、前缀和、DP 等模板;也会核对正文代码和测试文件是否一致。样例用于学习方法,不宣称覆盖 LeetCode 所有输入规格。提交时注意题目要求的 Solution、下标从 0 还是 1 开始,以及是否允许修改输入。

5. 今天先复习哪块

  • 只有 20 分钟:Hash map 的 Two Sum → get / in 的区别 → 关掉答案写一次。
  • 总搞混 DFS / BFS:打开遍历小实验,先预测下一个节点,再点下一步。
  • 模板会背但一变就不会:看每篇的“换个条件”,找反例,不急着加题量。

不必每次都刷新题。把一道旧题里没想明白的地方补上,也算一次有效的复习。算法只是准备的一部分,给项目和休息也留点时间。

6. 还有哪些中文讲解值得对照

同一个概念,换个人讲,可能就通了。下面几份资料各有侧重,不需要全看完;按眼下的问题选一篇就好。

你现在想弄明白什么 可以接着看
栈、队列、树到底是怎样一步步变化的 Hello 算法:先看对应章节的图,再回来看代码
会写模板,但不知道为什么这样写 灵茶山艾府的基础算法课:跟着双指针(two pointers)、二分、DP 等专题推一遍;题单里也有进阶题,不用全做
几道题很像,却不知道怎么放在一起理解 labuladong 的算法讲解:可以对照同一种方法在不同题目里怎么改;部分内容和工具有付费入口
DP 写出来了,但结果不对,不知道从哪查 代码随想录的 DP 基础:逐项核对状态、初值、递推和遍历顺序,再手算一个小例子

本站的例子和代码会自己推一遍、跑测试,也会保留参考链接。不同资料用的变量名、区间和状态定义可能不同,借鉴思路时别把几套写法直接拼在一起。如果哪篇还是没讲清楚,欢迎指出具体卡在哪一步。

本页术语 7
递归栈 recursion stack
递归过程中尚未返回的调用所占的栈空间
候选 candidate
召回阶段捞出来、等待精排挑选的内容
递归 recursion
函数直接或间接调用自身,把问题交给更小的子问题;要能走到终止条件
不变量 invariant
在约定的执行位置始终成立的条件,例如每轮更新后窗口里没有重复字符
前缀和 prefix sum
预先记录从起点累加的结果,用两个前缀之差求区间和
回溯 backtracking
尝试一个选择,继续搜索,返回后撤销选择,再试其他分支
双指针 two pointers
维护两个位置,利用输入条件决定怎样移动并排除候选