刷题进度¶
144 题进度清单,与 刷题方案 一一对应(一阶段 94 题 + 二阶段 50 道新题)。 复习/二刷清单(双指针 7、复习 43、Hard 二刷 3)与一阶段题目重复,按章节勾选即可,不重复列。 本文件在 git 中跟踪,并由 MkDocs 从
content/构建进站点。
进度速览¶
- 一阶段 94 题完成
- 二阶段 50 道新题完成
- 全量二刷通过(43 + 50 = 93 题筛一遍)
一、第一阶段:筑基速通(94 题)¶
数组:三个最基础的模板(4)¶
- 704 二分查找 —— 左闭右闭模板,后面所有二分题的地基
- 27 移除元素 —— 快慢指针原地覆盖,分清"读指针 / 写指针"
- 977 有序数组的平方 —— 对撞指针从两端取大值,倒着填结果
- 209 长度最小的子数组 —— 滑动窗口"右扩左缩"的原型
链表:dummy 头节点解决一切边界问题(6)¶
- 203 移除链表元素 —— dummy 头节点消灭"删的是头节点"这个特判
- 206 反转链表 —— 三指针
pre/cur/next;全场最高频手撕题,迭代和递归两版都要能闭眼写 - 24 两两交换链表结点 —— dummy + 一次操作三个指针,先画图再动手
- 19 删除倒数第N个结点 —— 快指针先走 n 步的固定套路
- 142 环形链表II —— 快慢指针找环,入环点的推导面试会让你证
- 160 相交链表 —— 双指针走完自己再走对方,消除长度差
哈希表:把暴力的第二层循环换成查表(6)¶
- 242 有效的字母异位词 —— 26 长度数组当哈希,比 HashMap 快
- 349 两个数组的交集 —— Set 去重
- 1 两数之和 —— HashMap 存"见过的值 → 下标",一趟遍历
- 15 三数之和 —— 排序 + 对撞指针,三处去重是全场最易错点
- 18 四数之和 —— 15 外面再套一层,注意剪枝和 int 溢出
- 202 快乐数 —— 用 Set 判环(本质就是链表找环)
字符串:只保留两道,KMP 不用手撕(2)¶
- 344 反转字符串 —— 对撞指针的最简形态
- 151 反转字符串中的单词 —— 整体反转再逐词反转,外加原地去多余空格
栈与队列:模板短,性价比最高的一章(6)¶
- 232 用栈实现队列 —— 双栈摊还 O(1),要能讲清摊还分析
- 20 有效的括号 —— 栈做"就近匹配"的原型
- 1047 删除字符串中的所有相邻重复项 —— 栈做"就近消除"
- 150 逆波兰表达式求值 —— 栈求后缀表达式
- 239 滑动窗口最大值 —— 单调队列。Hard 但 ★★★★★,必刷
- 347 前 K 个高频元素 —— 小顶堆求 Top K(是小顶堆不是大顶堆)
二叉树:全场最大的一章,砍掉 BST 增删改(16)¶
- 102 二叉树的层序遍历 —— BFS 队列 +
size控制层,这一题能变出七八道 - 226 翻转二叉树 —— 递归三部曲最简形态
- 101 对称二叉树 —— 同步递归比较左右子树
- 104 二叉树的最大深度 —— 后序求高度
- 111 二叉树的最小深度 —— 单侧空子树不算叶子,本章最常错的一题
- 110 平衡二叉树 —— 用
-1表示不平衡,提前剪枝 - 257 二叉树的所有路径 —— 第一次出现"回溯撤销"
- 112 路径总和 —— 递归带累减的目标值
- 236 二叉树的最近公共祖先 —— 后序返回值设计的巅峰,必考
- 98 验证二叉搜索树 —— 中序递增 / 上下界传参两种写法
- 235 二叉搜索树的最近公共祖先 —— 利用有序性,比 236 简单,对照着记
- 538 把二叉搜索树转换为累加树 —— 反中序(右 → 中 → 左)
- 700 二叉搜索树中的搜索 —— BST 递归入门,2 分钟
- 108 将有序数组转换为二叉搜索树 —— 二分递归建树
- 105 从前序与中序遍历构造二叉树 —— 切割区间的边界最容易写错
- 617 合并二叉树 —— 同步递归两棵树;踩坑:
root1 = root2是搬运不是嫁接,会自己加自己翻倍
回溯:一个模板通吃十道题(10)¶
- 77 组合 —— 模板 +
startIndex的原型 - 216 组合总和 III —— 77 加上和值剪枝
- 17 电话号码的字母组合 —— 多个集合之间取组合,不需要 startIndex
- 39 组合总和 —— 元素可重复取:递归传
i而不是i+1 - 40 组合总和 II —— 元素有重复但不可重取:排序 + 同层去重
- 131 分割回文串 —— 把"切割"映射成组合问题
- 78 子集 —— 收集所有节点,不只叶子
- 90 子集 II —— 子集 + 同层去重
- 46 全排列 ——
used[]标记已选;另写交换法变体(AC 26/26) - 47 全排列 II —— 排列 + 同层去重,和 40 的去重位置对照着记
贪心:全书最该砍的一章(9)¶
- 455 分发饼干 —— 排序 + 贪心配对
- 376 摆动序列 —— 记录上下坡的方向变化
- 53 最大子数组和 —— 前缀和为负就丢弃(也是 DP 入门题)
- 122 买卖股票的最佳时机 II —— 所有上涨区间全吃掉
- 55 跳跃游戏 —— 维护"最远可达位置"
- 45 跳跃游戏 II —— 按"层"跳,本质是 BFS
- 435 无重叠区间 —— 按右端点排序,区间贪心模板
- 763 划分字母区间 —— 记录每个字母最后出现的位置
- 56 合并区间 —— 按左端点排序,和 435 对照着记
动态规划:唯一要刷到 3/4 的一章(26)¶
- 509 斐波那契数 —— DP 五部曲的最简载体
- 70 爬楼梯 —— 和 509 同式,重点是理解"为什么是它"
- 746 使用最小花费爬楼梯 —— 初始化的坑:从 0 还是从 1 起跳
- 62 不同路径 —— 二维 DP 入门
- 63 不同路径 II —— 加障碍,初始化要中断
- 416 分割等和子集 —— 01 背包判定型,容量倒序遍历
- 494 目标和 —— 转成"求子集和为定值的方案数"
- 1049 最后一块石头的重量 II —— 转成尽量均分的 01 背包
- 518 零钱兑换 II —— 完全背包求组合数:外层物品、内层容量
- 377 组合总和 IV —— 求排列数:外层容量、内层物品。和 518 对照,这是背包最大的坑
- 322 零钱兑换 —— 完全背包求最值,初始化为
amount+1 - 279 完全平方数 —— 322 换一套物品集合
- 139 单词拆分 —— 字符串上的完全背包
- 198 打家劫舍 —— 线性 DP 状态定义的典范
- 213 打家劫舍 II —— 环形:拆成两段线性各跑一次
- 337 打家劫舍 III —— 树形 DP,返回长度为 2 的数组
- 121 买卖股票的最佳时机 —— 只能买一次:维护历史最低价
- 123 买卖股票的最佳时机 III —— 最多两次:五个状态
- 188 买卖股票的最佳时机 IV —— 最多 k 次:把 123 的状态数组化
- 300 最长递增子序列 —— O(n²) 必会,能提一句贪心 + 二分的 O(n log n)
- 718 最长重复子数组 —— 连续:不匹配就归零
- 1143 最长公共子序列 —— 不连续:不匹配可以继承。和 718 对照记
- 392 判断子序列 —— 1143 的特例,也能双指针秒杀
- 72 编辑距离 —— 二维 DP 的终点站,三个状态来源
- 647 回文子串 —— 区间 DP / 中心扩展两种解法
- 516 最长回文子序列 —— 区间 DP,遍历顺序要倒着来
单调栈:三道题够了(3)¶
- 739 每日温度 —— 单调递减栈的原型,栈里存下标
- 496 下一个更大元素 I —— 739 加一张映射表
- 42 接雨水 —— 单调栈 / 双指针两种解法。Hard 但 ★★★★★,必刷
图论:只保留 DFS 灌水和拓扑排序(6)¶
- 200 岛屿数量 —— 灌水法的原型
- 695 岛屿的最大面积 —— 灌水带返回值
- 130 被围绕的区域 —— 反向思维:从边界开始灌水
- 207 课程表 —— 拓扑排序判环(入度表 + 队列)
- 210 课程表 II —— 207 加输出顺序,同一份代码
- 547 省份数量 —— 并查集入门(也能 DFS)
二、第二阶段:Hot100 验收(50 道新题)¶
新题 47 道¶
哈希 / 数组 / 技巧(10):
- 49 字母异位词分组 —— 排序后的字符串当 key
- 128 最长连续序列 —— Set + 只从序列头开始扩,O(n)
- 189 轮转数组 —— 三次反转
- 238 除自身以外数组的乘积 —— 前缀积 × 后缀积
- 136 只出现一次的数字 —— 异或自反
- 169 多数元素 —— 摩尔投票
- 75 颜色分类 —— 三路快排的 partition
- 31 下一个排列 —— 从后找升序对 → 交换 → 反转后缀
- 287 寻找重复数 —— 把数组看成链表,快慢指针找环
- 118 杨辉三角 —— 送分题
双指针 / 滑动窗口(5):
- 283 移动零 —— 快慢指针原地覆盖
- 11 盛最多水的容器 —— 对撞指针,移动短板
- 3 无重复字符的最长子串 —— 滑动窗口,★★★★★
- 438 找到字符串中所有字母异位词 —— 定长窗口 + 计数数组
- 560 和为 K 的子数组 —— 前缀和 + 哈希计数,不是滑动窗口(有负数)
矩阵(4):
- 73 矩阵置零 —— 用首行首列当标记位
- 54 螺旋矩阵 —— 四边界收缩模拟
- 48 旋转图像 —— 转置 + 左右翻转
- 240 搜索二维矩阵 II —— 从右上角开始走 Z 字
二分查找(5):
- 35 搜索插入位置 —— 退出时
l就是插入位置 - 74 搜索二维矩阵 —— 二维拍平成一维
- 34 在排序数组中查找元素的第一个和最后一个位置 —— 左右边界各二分一次
- 33 搜索旋转排序数组 —— 先判哪半有序,★★★★★
- 153 寻找旋转排序数组中的最小值 —— 和 33 同一套判断
链表(7):
- 234 回文链表 —— 找中点 + 反转后半段
- 141 环形链表 —— 142 的简化版
- 21 合并两个有序链表 —— dummy + 双指针归并
- 2 两数相加 —— 进位处理,注意最后一次进位
- 138 复制带随机指针的链表 —— 哈希映射 / 原地穿插两种
- 148 排序链表 —— 归并排序,要求 O(1) 空间时用自底向上
- 146 LRU缓存 —— 全场最高频设计题,哈希 + 双向链表要能一次写对
栈与堆(3):
- 155 最小栈 —— 辅助栈同步存最小值
- 394 字符串解码 —— 双栈处理嵌套
- 215 数组中的第 K 个最大元素 —— 堆 / 快速选择,★★★★★
二叉树(6):
- 94 二叉树的中序遍历 —— 递归 + 栈模拟两版
- 543 二叉树的直径 —— 后序 + 全局变量
- 230 二叉搜索树中第 K 小的元素 —— 中序第 k 个
- 199 二叉树的右视图 —— 层序取每层最后一个
- 114 二叉树展开为链表 —— 前序 / 反向后序原地改指针
- 437 路径总和III —— 前缀和 + 哈希,树上版的 560
图论 / 字典树(2):
- 994 腐烂的橘子 —— 多源 BFS
- 208 实现 Trie —— 前缀树基本操作
回溯(2):
动态规划(3):
- 152 乘积最大子数组 —— 同时维护最大和最小(负负得正)
- 64 最小路径和 —— 62 的加权版
- 5 最长回文子串 —— 中心扩展 / 区间 DP
Hard 新增(3)¶
- 76 最小覆盖子串 —— 滑动窗口的天花板,
need/window双计数表 - 4 寻找两个正序数组的中位数 —— 二分割点,写不出来至少要能说归并法 O(m+n)
- 25 K个一组翻转链表 —— 206 的分组版,字节最爱考
三、错题本¶
按 刷题方案 的「错题本怎么记(一行就够)」:
- 一刷收口:
LC 题号 题名 | 套路 | 卡点:一句话 - 二刷 60 秒说不出思路的题记
★,进入三刷 - 三刷只看错题本
| 题号 | 套路 | 卡点 | 状态 |
|---|---|---|---|
| LC 977 有序数组的平方 | 对撞指针 | 题设说“返回新数组”却硬要原地改(nums 既读又写,left 钉死 0 不移动),46% 随机用例挂;标准解是新数组从后往前填、源只读 | 2026-09-02 已 AC 137/137 |
| LC 322 零钱兑换 | 完全背包 | 遍历顺序搞反了,求组合数要"先物品后容量" | |
| LC 142 环形链表II | 快慢指针 | ① 相遇点≠环入口,缺第二阶段(ptr=head 与 slow 同速走);② 判相遇用值比较 slow.val==fast.val 不可靠,必须引用相等 |
2026-10-28 已 AC 18/18 |
| LC 160 相交链表 | 双指针换轨 | 换轨时 if(pA==null) pA=headB; 后又无条件 pA=pA.next;,跳过了对方头节点;换轨那一步不能再 next,须用 else 分支 |
2026-10-28 已 AC 41/41 |
| LC 15 三数之和 | 排序+对撞指针 | 只写了内层去重❷❸,漏了外层❶ i>0 && nums[i]==nums[i-1] continue,同一固定值重复枚举 → 重复三元组([-1,0,1] 收两次);剪枝用 >0 不是 >=0(否则丢 [0,0,0]) |
2026-10-28 已 AC 316/316 |
| LC 18 四数之和 | 排序+双层+对撞 | ① j 去重条件写成 j>0(应为 j>i+1),相邻同值时第一个 j 被跳过 → [0,0,0,0] 返回空;② long sum = a+b+c+d 仍 int 溢出(表达式内先按 int 算),须 (long) a + b + c + d |
2026-10-28 已 AC 295/295 |
| LC 239 滑动窗口最大值 | 单调队列 | ① 出结果时用 pollFirst()(弹出)而非 peekFirst()(只读)→ 最大值被弹走,示例1 输出 [3,-1,5,3,6,7];② 弹队尾用 <= 不是 <(相等时候选留新不留旧) |
2026-10-28 已 AC 53/53 |
| LC 347 前 K 个高频元素 | 小顶堆 TopK | swap 写成 heap[j] = heap[i](复制而非交换,原 heap[j] 丢失)→ 结果值重复 [1,1]/[4,4,4];正确三步 t=heap[i]; heap[i]=heap[j]; heap[j]=t |
2026-10-28 已 AC 26/26 |
| LC 111 二叉树的最小深度 | BFS 层序 | ① count++ 放进 for 内层 → 变成"节点计数"而非"层号";且叶子处只 break 不 return,while 继续跑导致返回值错乱。层号必须每层 +1,找叶子要 return ② offer(pop.left) 没判 null → ArrayDeque 拒绝 null 直接 NPE |
2026-10-30 已 AC 53/53 |
| LC 98 验证二叉搜索树 | BST 中序 / 上下界 | 中序版连踩:① 末行写成 root.left(右子树没查);② 漏 prev = root.val(检查永远失效);③ > 漏等号;④ 方向写成 prev < root.val(②和④互相掩盖,看着全对其实全错);⑤ prev 当 Integer 参数传 → 子层更新传不回父层([5,6,null]、[1,1] 误判 true) |
2026-10-30 已 AC 86/86(两版) |
| LC 538 BST 转累加树 | 反中序 | 分离版里内部递归调了入口方法 convertBST(而非 cBST)→ 入口的 sum = 0 每层执行,累加全被清零,输出与输入完全相同 |
2026-10-30 已 AC 215/215(两版) |
| LC 108 有序数组转 BST | 二分递归建树 | 三坑连锁导致 StackOverflow:① 终止条件写 left == right(应 left > right)② mid 写成 left + (right + left) / 2(应 right - left)③ 子树区间忘了排除 mid(build(left, mid)/build(mid, right))。口诀:mid 是根,左 mid-1 右 mid+1 |
2026-10-31 已 AC 31/31 |
| LC 216 组合总和III | 回溯+剪枝 | 出口条件写成一行 path.size()==k && remain==0:remain 不为 0 时不 return、继续进 for,need=0 把候选上限撑到 10+,往 path 里塞 10/11 幽灵值——结果虽对但 k=2,n=45 递归 3203 次对比分开写 45 次(71 倍)。改成:外层 if 管"停"、内层 if 管"收"、return 无条件 |
2026-11-03 已 AC 19/19 |
| LC 746 最小花费爬楼梯 | DP(虚拟层/流派) | v1 返回 dp[n-1]:楼顶是【虚拟层】不是数组末位——收顶可从 last 跳1 或 从 last-1 跳2 且都免费 → min(dp[n-1], dp[n-2])。到达付/离开付两种流派都行但收账格子错位一档,选定流派后"初始化/循环上限/返回值"全套按流派一致 |
2026-11-07 已 AC 286/286 |
| LC 435 无重叠区间 | 区间贪心 | v1 WA 10/60:① 无排序([5,6] 先在场全堵死)② 判定 cur[1]!=start——相接/有间隙都误判(应 start >= keepEnd 保留、< 才删)③ 删除后 cur=intervals[i] 跟随被删者——参照必须恒为 keptEnd。v2:keep 只加不清 + del=n-keep 补集 |
2026-11-06 已 AC 60/60 |
| LC 47 全排列 II | 回溯(去重/撤销粒度) | v1 两坑:① used = new boolean[](掀桌重开)——把"分支结束时的全 F 终态"用在分支进行中的每一步,祖先的 true 被撕 → 跨层连用锁死失效,[1,1,1]/[1,2,2] 幻影(撤销粒度=选择粒度,只撤自己那一笔"used[i]=false";与 90 漏撤销互为镜像:撤太少/撤太多);② 去重判 i > 1,第一对相邻同值(i=1 对 i=0)漏网 → i > 0。start 在排列里只当层号计时器,管挡人的是 used |
2026-11-04 已 AC 33/33 |
| LC 46 全排列 | 回溯(排列/值身份vs下标身份) | used 用 HashSet 装【值】:46 题设互异碰巧可用,重复值输入会归零([1,1,2] 收 0 个——第一个 1 进 Set 后第二个 1 永远被拦,path 凑不满)。used 的身份必须是下标(boolean[]),值只是下标的影子;另学交换法:path=数组前缀、池=[start..]、撤销=同对 swap 再来一遍、收账必须拷快照 | 2026-11-04 已 AC 26/26(交换法) |
| LC 90 子集 II | 回溯(撤销/收账/止损三分) | ① 漏了撤销行 path.remove(size-1):单链树([1,1])碰巧全对,真分叉树([1,2,2])立刻脏——兄弟分支踩脏 path → 缺 [2]/[2,2]、多幻影 [1,2,2,2]/[1,2,2,2,2](去重墙挡不住跨分支传染);② 概念澄清:收账无条件在第一行(答案=每个节点),break 是剪枝(传染至层尾),continue 只死单支 |
2026-11-04 已 AC 21/21 |
| LC 77 组合 | 回溯模板 | ① start=0 起点写错(候选应为 [1,n]),数量对但值全偏 1(C(4,2)=6 个 [0..3] 组合),范围检查才抓到;② JDK17 无 List.removeLast()(JDK21+ 才有),命令行编译失败,需 remove(path.size()-1);③ 剪枝闭边界用 < 时要写 n-need+2,用 <= 时写 n-need+1——两种写法不能混 |
2026-11-04 已 AC 27/27 |
| LC 377 组合总和 IV | 背包(排列数) | 外层 for 上界 < target 应 <= target——目标格 dp[target] 从未计算,7 个用例 4 个全返回 0;213 复盘校准规则:真正规矩是"比较符必须匹配调用合同"——自增递推"最后一个目标位"默认 <=;参数化区间选半开合同(含头不含尾,如 Java subList)就用 <。377 翻车的本质是"想含 target 却写了 <"的合同内部不一致。另:空区间 l==r 有自然写法是半开合同的工程红利 |
2026-11-09 已 AC 18/18 |
| LC 198 打家劫舍 | 线性 DP | 地基行语义错叠:dp[0] 写成 min(nums[0],nums[1])(想当然"两栋互补")——dp[0] 的世界只有第 0 栋,必须 =nums[0];反例 [5,1,10] 正解 15 vs 错版 11(min 偷换"偷 0 资格"被 dp[2] 踩回)。harness 8 用例全漏因 min/max 多数场景碰巧等效——地基行每格都必须能独立说出前缀语义 | 2026-11-09 已 AC 70/70 |
| LC 105 前序+中序建树 | 双序列分治 | pix(中序下标)被直接当前序右边界用:build(preL+1, pix) 应为 build(preL+1, preL+leftLen),leftLen = pix - inL。只有 preL == inL(根层)时碰巧对,深层重复包含根 → StackOverflow。反例 pre=[1,2,3], in=[2,3,1] |
2026-10-31 已 AC 203/203 |