8 个里程碑覆盖 Hot100 全题型,按依赖顺序学,每题都站在上一题的肩膀上
校招面试 70% 的算法题来自 LeetCode Hot100,但乱序刷等于自找苦吃。本路径按「哈希→双指针→滑窗→栈→二分→链表→树→回溯→图→DP→贪心」的技术依赖链排列,每道题都告诉你它复用了前一题的哪个具体技巧。59 道精选覆盖所有核心题型,8 周系统过完,秋招/春招不再临时抱佛脚。
适合:准备秋招/春招、要系统过一遍高频的本科生
哈希表把「查找另一半」从 O(n) 枚举压到 O(1) 查表;前缀和把「区间求和」转换为两点相减。两招合用,能解决绝大多数数组加速题:先想能否建 Map/Set 存已见值,再想能否用前缀数组把多次查询预处理成一次。
哈希开局:用 Map 把「找另一半」从 O(n²) 枚举降到 O(1) 查表,是所有哈希加速题的原型。
前题单值查找;本题改为「按特征归堆」:排序后字母序作 key,Map 收集同族字符串。
前两题用 Map;本题用 Set + 「只从序列起点开始数」剪枝,O(n) 找最长连续序列。
本题把哈希加速搬入前缀和:Map 记 prefixSum 出现次数,sum[i]-sum[j]=k 变 O(1) 查表。
前缀和记累计加法;本题换前缀乘积:左缀数组 × 右缀变量,一次遍历原地替换。
计数可排序;本题用桶排(下标=频次)替代 O(n log n) 排序,Top-K 降到 O(n)。
前几题都建辅助结构;摩尔投票 O(1) 空间:同数+1异数-1,票数归零换候选人。
双指针分两类:同向快慢指针处理原地操作,对撞指针利用排序后的单调性收窄搜索空间。滑动窗口是同向双指针的进阶——右指针扩窗满足条件,左指针收窗找最优,两者共同维护一个「合法窗口」。
哈希记录值;双指针原地操作:慢指针指向可写位置,快指针扫非零元素,无需额外数组。
快慢同向;本题对撞:左右指针从两端出发,矮边向内收——贪心证明每步不会错过最大答案。
两数之和用 Map;本题固定一个数后用对撞双指针代替第二层枚举,排序后跳过重复值去重。
对撞双指针再进阶:左右各维护历史最大高度,较矮一侧确定积水量,按桶短板逐步收窄。
对撞是固定端;滑窗动态收缩:右扩到重复就左缩,Set 记窗内字符,保持无重复最长窗口。
前题变长窗口;本题定长(等于 p 长度):窗口平移时右加左减,频次归零即异位词。
定长窗口不够灵活;本题变长:右扩到覆盖所有字符才收左,收到不覆盖再扩右——双指针控宽。
普通栈解决嵌套匹配与上下文保存(括号、字符串解码);单调栈解决「找左/右侧第一个更大/更小」——栈内保持单调性,一旦被破坏就弹出并在弹出时结算答案。两种用法从模板到变体都要烂熟。
滑窗处理连续区间;栈处理嵌套配对:左括号压栈,右括号弹出比较,不匹配即非法。
单栈记值;辅助栈同步记最小值:每次 push 时压入 min(新值, 辅助栈顶),O(1) 查最小。
辅助栈存最小值;本题栈存上下文:遇 [ 压当前串+倍数,遇 ] 弹出拼接,是递归的迭代写法。
前几题栈记值;单调栈记待解决索引:栈内保持温度单调递减,遇更高温度弹出结算距离差。
每日温度找「右侧首个更大」;本题找「两侧首个更小」,单调递增栈弹出时以弹出高度计矩形面积。
有效括号只判合法性;本题求最长合法长度:栈记索引,弹出后用当前索引减栈顶索引得长度。
单调栈处理单次遍历;滑动窗口需移除过期元素:改用单调递减双端队列,队头是窗口最大值。
二分不只用于有序数组——任何「答案域单调」的场景都能二分;链表操作靠指针而非下标,必须熟练画出指针变化图。两类题的共同训练目标是「边界意识」:二分的开闭区间、链表的哑节点与空指针处理。
栈是线性扫描;二分靠有序性:旋转后至少一半有序,判断目标在有序半段再收窄,O(log n)。
前题找一个目标;本题找左右边界:写两次二分分别锁「第一个≥target」和「最后一个≤target」。
边界二分在单数组;本题对两数组做分割线:让两侧元素数相等且左全小于右,O(log min(m,n))。
数组靠下标原地操作;链表靠指针:三指针(prev/cur/next)一次遍历反转,是链表操作基础骨架。
反转快慢步长相同;环检测步长不同:快走 2 慢走 1,有环必追上,O(1) 空间判环。
链表快慢指针检测环;本题把数组值当 next 指针,重复数即虚拟链表环入口,套 Floyd 算法。
单链表操作;本题双链表归并:哑节点 + 双指针比较接小节点,是归并排序的核心子步骤。
归并两条已排序链表已掌握;本题全排序:快慢指针找中点 + 递归分割 + 归并,O(n log n)。
树的所有题型都能用「递归三问」拆解:①函数返回什么,②参数是什么,③单层如何处理左右子树。前序处理当前节点,后序收集子树信息,BFS 层序用双层循环。堆(优先队列)是树的工程化应用,O(log k) 维护 Top-K 结构。
链表递归线性展开;树递归分叉:递归三问框架,后序取左右最大深度+1,是所有树题的入门模板。
深度向下传信息;本题前序:先交换当前节点左右子树,再递归——前序逻辑处理当前节点再下探。
最大深度只返回单路径;直径=左最深+右最深:后序回传深度,全局变量记最大直径。
前三题 DFS 纵向;层序换 BFS:队列+while 外层+for 内层记 size,是所有层序变体的母版。
BFS 遍历所有节点;BST 验证利用中序有序性:递归传合法值域 (min,max),比中序排序判断更优雅。
BST 按值域剪枝;普通树 LCA 无值域:后序递归,左右各找到目标则当前节点为 LCA。
LCA 后序收集两侧节点;本题反向建树:前序首元素定根,中序定位根后递归划分左右区间,O(n) 建树。
前序定根划分子树;最大路径和改回后序:回传单侧最大链,左+根+右更新全局最大,负贡献剪 0。
归并两条链表已掌握;本题 K 条:小根堆同时维护 K 个指针,每次弹最小节点接入,O(n log k)。
回溯是「带撤销的树形 DFS」:进入递归前选,返回后撤。图的 DFS/BFS 是回溯思想在网格/邻接表上的应用——多出「已访问标记」防止重复。拓扑排序把有向无环图的依赖关系线性化,Kahn 算法(BFS 入度归零出队)是面试首选写法。
树的递归结构固定;回溯是带撤销的树形递归:每层枚举按键字母,入递归前选,回来后撤销。
电话号码每层可选集固定;组合总和可重复选:同一数可再选(不加 index+1),目标归零即收。
组合不关心顺序;排列关心顺序:used 数组标记已选元素,每层从头枚举但跳过已用,选满即收。
全排列选满才收;子集每层状态都是合法答案:进入递归立刻收集,再枚举后续位置前剪枝。
子集在抽象树上回溯;单词搜索在网格 DFS:四方向递归,访问时标记已用,返回后还原原字符。
单词搜索从单一起点;岛屿数量要枚举全部起点:双循环找陆地,DFS 把整片岛染色,计连通分量。
岛屿 DFS 找连通分量;课程表检测有向图是否有环:Kahn 拓扑排序,入度为零出队,出队数<n 即有环。
一维 DP 的三步建模:①定义 dp[i] 的含义,②写出「最后一步」状态转移方程,③确定初始值与遍历方向。从爬楼梯「两种走法」到 0-1 背包「选与不选」,框架不变,变的只是转移方程的来源。
图的遍历是逐层扩散;DP 是逐格转移:爬楼梯 dp[i]=dp[i-1]+dp[i-2],先写「最后一步」再填表。
爬楼梯相邻状态可转移;打家劫舍相邻不能同取:dp[i]=max(dp[i-1], dp[i-2]+nums[i]),选与不选二择。
打家劫舍每格独立决策;最大子数组连续约束:Kadane dp[i]=max(nums[i], dp[i-1]+nums[i]),负前缀直接丢弃。
最大子数组只维护最大值;乘积需同时维护最小值(负×负=正):同时记结尾最大积和最小积。
子数组连续;子序列不连续:LIS dp[i]=1+max(dp[j] for j<i 且递增);再用二分替换内层到 O(n log n)。
LIS 是最长序列;零钱兑换是最少次数:完全背包,dp[i]=min(dp[i-coin]+1),枚举每个面值。
零钱兑换枚举硬币面值;单词拆分枚举字典词:dp[i] 前 i 字符能否拆分,外层终点 i 内层分割点 j。
二维 DP 把状态扩展到坐标 (i,j) 或字符串双指针,转移方程从三个方向取最优。贪心跳过 DP 枚举——只要能证明「局部最优推全局最优」就用贪心。位运算是特化技巧,XOR 的对称性解决「孤独值」问题。
一维 DP 状态是下标;二维状态是坐标 (i,j):dp[i][j]=dp[i-1][j]+dp[i][j-1],边界全填 1。
不同路径数方案;本题求最小路径和:同框架把加法改为取 min+grid[i][j],路径值累加。
路径和是坐标位移;编辑距离是字符串变换:dp[i][j] 为 s[0..i]→t[0..j] 最少操作,增删改三向取 min+1。
编辑距离二维 DP;0-1 背包同属二维:dp[i][j]=前 i 个数能否凑满容量 j,等和拆分=总和/2 的背包。
0-1 背包逐格转移;跳跃游戏贪心:维护能到达的最右边界 maxReach,无需枚举所有状态组合。
跳跃按当前位置贪心;区间合并按左端点排序后贪心:新区间左端≤已合并右端即合并,否则新开区间。
区间合并需排序预处理;异或无需排序:XOR 满足交换律结合律,任何数与自身异或为 0,剩下孤独值。