ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LeetCode热题100高效刷题指南:三轮刷题法与题型判断力提升

LeetCode热题100高效刷题指南:三轮刷题法与题型判断力提升 聊到算法面试LeetCode 热题 100 是个避不开的话题。它不像某些争议题那样让人又爱又恨反而更像一张被反复验证过的地图——很多面试官出的题目要么直接从这张清单里挑要么在它的基础上改几个条件。我前后把这份题单刷了三轮第一轮用了将近两个月第三轮只用了五天。回头来看真正让我成长的不是题量而是顺着这份清单建立起来的题型判断力。这篇文章不讲答案汇总而是把我在规划、拆题、踩坑、复盘时沉淀下来的一套方法写出来给正在对付热题 100 的你一条可以照着走的路径。1. 热题 100 究竟是一份什么清单1.1 它是怎么来的里面装了什么LeetCode 热题 100 的题目来源从产品逻辑上看并不神秘。中文站的运营团队会根据真实面试中被提及的频率、用户在题解区的讨论热度、提交量以及收藏夹数据做一轮综合排序再人工筛选出 100 道具有代表性的题目。它没有一个官方教学大纲的架子但在过去几年里被大量求职者当作算法面试的基准清单。你可以把它理解成算法面试圈里的主干道地图不能说每条巷子都覆盖了但主要路线基本都在里面。从题型构成看这份清单覆盖了数组、字符串、哈希表、双指针、滑动窗口、二分查找、栈、队列、链表、二叉树、图、回溯、动态规划、贪心等十几个专题。难度分布也很有意思简单题大概占三分之一中等题超过一半困难题十几道。这说明它并不是拿来劝退的而是让你在一个相对可控的范围内逐步深入。如果你能把 Hot 100 全部 AC再去做周赛前三题手感是很明显的。1.2 为什么偏偏是 100 道而不是题海很多人会问热题就 100 道够用吗我的结论是如果目标是应对绝大多数算法面试够如果目标是在竞赛里拿名次那当然不够。热题 100 的设计初衷是抓主干不是堆数量。你把这个清单里的考点横向扫一遍就会发现即使题目各不相同核心套路的组合方式却很有限。以数组题为例一大半热门题就是三件事用哈希表做空间换时间用双指针把 O(n²) 降成 O(n)用滑动窗口处理连续子段问题。树的题目则是递归遍历模板换着花样出。动态规划更是如此爬楼梯、打家劫舍、最长递增子序列本质都是状态定义加转移方程。把 100 道题刷透实际上是把几十套基础模板吃透。你从两数之和走向三数之和再走向最长无重复字符的子串就能明显感觉到所谓新题不过是旧模板换了件外套。2. 刷题前的准备先想清楚再动手2.1 花两三天自测数据结构和算法基本功开刷之前我强烈建议你先做一次自测。不要上来就打开网页做题否则很容易在第 20 题左右崩掉。自测不要求你手写红黑树而是要确认最常用的数据结构能否随手用起来。我会问自己五个问题能不能 10 分钟手写一个 LRU 缓存能不能用两种方式实现二叉树前序遍历能不能说清楚哈希表在常用语言里遇到冲突时分别怎么处理能不能讲明白递归过程中栈帧的变化能不能快速写出快排的 partition。如果这五个问题都能答上来可以直接开始刷题如果卡壳先花一周补基础。我把这套自测整理成一个可对照的表格领域自测点掌握标准数组与链表增删查的时间复杂度能说清数组随机访问 O(1)、链表插入删除 O(1) 的代价哈希表冲突处理、负载因子能说清链地址法、开放寻址以及为什么扩容栈与队列单调栈、队列模拟能手动模拟“柱状图最大矩形”的过程树递归遍历、层序遍历、BST 性质能以空节点为边界写对递归终止条件图邻接表表示、BFS/DFS 模板能独立完成一次网格类连通块统计自测的目的不是制造焦虑而是避免刷题过程中因为基础不牢而反复卡死。真到了那一步再回头补基础成本远比你想象得高因为你很难分辨当前问题到底出在算法思路上还是数据结构不熟上。2.2 选对语言配好本地调试环境语言选择的第一原则面试用什么刷题就用什么。如果岗位明确要求 C就老老实实把 STL 常用容器练熟如果是 Java 岗位那 HashMap、PriorityQueue、ArrayDeque 这些 API 必须处于下意识水平。我自己更习惯用 Python 来快速验证思路用 Java 做工程化复写。Python 写起来快适合第一轮快速跑通逻辑Java 类型清晰适合准备手写代码的场景。但我不建议你在同一轮刷题里频繁切换语言那样会分散精力容易变成学语言而不是学算法。本地调试环境我建议一定要配。我每次新建一个仓库按专题建目录每道题单独建文件文件头部用注释记录题目编号、专题、思路和复杂度。调试时不要一上来就开 IDE 断点先用打印关键变量的方式确认中间结果符合预期再考虑边界。LeetCode 网页上有测试用例但本地环境的好处是你可以反复修改慢慢形成自己的测试集。2.3 安排好节奏建立你的刷题记录热题 100 如果每天刷 2 道需要 50 天这个节奏适合在职或者课业重的朋友如果每天能保证 4 道大概 25 天过完第一轮但需要周末集中时间。我不建议把战线拉得太长因为题目的遗忘速度远比想象中快。第一遍形成的解题惯性会在两周后变得模糊所以记笔记非常重要。我用的记录模板很简单日期、题目编号与难度、所属专题、思考用时、是否看题解、错误点、关键收获。看起来枯燥复刷时极有用。到了第二轮基本只需要看错误点和关键收获就能快速恢复记忆。这个习惯坚持下来你会在第三轮发现自己分类题目的速度变快因为记录帮助你建立了题与题之间的索引。3. 按专题把热题 100 拆开看3.1 哈希表与双指针空间换时间的主战场热题 100 里哈希表和双指针经常成对出现。两数之和是哈希表应用的第一课遍历数组时把当前位置的值存入哈希表后续元素只要查 target - nums[i] 在不在表中即可整体复杂度 O(n) 时间、O(n) 空间。这里有个容易被忽略的细节先查表再存当前值这样才能避免同一个元素被重复使用。三数之和则是双指针的典型场景。数组排序后固定一个数用左右指针去找另外两个数。难点不是找到解而是去重。很多人第一次能写对第二次遇到重复元素就错原因在于不知道跳过重复元素应该动指针而不是删解。正确做法是当固定指针和上一个值相同时直接跳过left 和 right 在找到一组解后分别移动到下一个不同的值上。这个细节面试时很加分。无重复字符的最长子串是滑动窗口的代表题。窗口右边界不断扩展左边界根据重复字符上一次出现的位置收缩。模板我一般这样写def lengthOfLongestSubstring(s: str) - int: last {} left 0 ans 0 for right, ch in enumerate(s): if ch in last and last[ch] left: left last[ch] 1 last[ch] right ans max(ans, right - left 1) return ans这类题一旦吃透再遇到最长连续序列、合并区间时思路会顺畅很多。更重要的是你能意识到哈希表的价值不只是查重它适合在需要 “记录位置” 或 “检查历史状态” 的时候充当辅助工具。3.2 二分查找从排序数组到二分答案二分这块很多人的认知停留在排序数组查找但热题 100 里有一类题是二分的另一面二分答案。LeetCode 073 爱吃香蕉的狒狒就是这么一道题。题目给一堆香蕉 piles 和总时间 h每小时只能选一堆香蕉吃 k 根求刚好在 h 小时内吃完的最小 k。如果直接枚举 k要遍历到 max(piles)数据一大就会超时。这时注意 k 和耗时之间存在单调关系k 越大耗时越短。于是可以在 [1, max(piles)] 上二分这个最小速度。check 函数的写法是重点。对每堆香蕉 p吃掉它需要 ceil(p / k) 小时在 Python 里可以写成(p k - 1) // k把所有堆耗时加起来判断是否小于等于 h。边界条件不能忘如果 len(piles) 大于 h那 k 无论多大都吃不完因为每小时最多只能消耗一堆。这道题的价值在于帮你建立 “二分答案” 的思维比单纯背模板重要得多。我习惯用左闭右开的二分模板def minEatingSpeed(piles, h): def can(k): return sum((p k - 1) // k for p in piles) h lo, hi 1, max(piles) 1 while lo hi: mid (lo hi) // 2 if can(mid): hi mid else: lo mid 1 return lo这个模板能避免死循环。熟练之后它可以直接迁移到分割数组最大值、搜索旋转排序数组等题目上。你真正要理解的是满足单调性的问题就存在二分的空间。3.3 栈与表达式括号真不是无脑题基本计算器这道题LeetCode 的版本里只有加减法和括号没有乘除但它每年都会劝退一批人。难点不在于做算术而在于括号带来的符号翻转。我推荐用一个栈来处理两层信息遇到左括号时把当前累计结果和当前符号压栈进入括号内部后单独维护新的结果遇到右括号时把括号内的结果乘以外部符号再加回之前保存的结果。本质上就是在模拟函数调用时的上下文保存。理解了这一点代码写起来非常顺。def calculate(s: str) - int: stack [] res 0 sign 1 num 0 for ch in s: if ch.isdigit(): num num * 10 int(ch) elif ch : res sign * num num 0 sign 1 elif ch -: res sign * num num 0 sign -1 elif ch (: stack.append(res) stack.append(sign) res 0 sign 1 elif ch ): res sign * num num 0 res * stack.pop() res stack.pop() res sign * num return res再往后带乘除的基本计算器 II 可以用数字栈加运算符栈处理核心是乘除优先级高于加减所以要缓存上一个数字。热题 100 里的柱状图中最大的矩形、接雨水则都要用到单调栈。单调栈不是背个模板就能通杀而是要知道栈里保存的应该是下标而不是值因为计算宽度时需要下标。每次弹出栈顶时以当前柱子为高、以下标差为宽一次一次地算面积这就是这类题的统一框架。3.4 树与图DFS 和 BFS 三板斧树和图是热题 100 的另一块重头戏也是面试中最容易出变种题的部分。树的题目DFS 和 BFS 两套模板必须形成肌肉记忆。DFS 递归写法的核心是先处理空节点再递归左右子树注意对于二叉搜索树类题目往往需要一个额外的前驱指针记录上一个节点因为中序遍历结果应当递增。BFS 层序遍历则用一个队列循环里先记录当前层大小再一次性处理完整个层这样可以保证每一层都单独一组。图论题里腐烂的橘子是极其典型的代表。题目说初始可能同时有多个烂橘子每过一分钟向上下左右扩散一格。为什么用 BFS 而不是 DFS因为所有烂橘子同时传播天然符合 BFS 的层次顺序每一层对应的就是“这一分钟”新腐烂的橘子。如果一开始就把所有烂橘子入队那就是多源 BFS。from collections import deque def orangesRotting(grid): m, n len(grid), len(grid[0]) q deque() fresh 0 for i in range(m): for j in range(n): if grid[i][j] 2: q.append((i, j, 0)) elif grid[i][j] 1: fresh 1 dirs [(1, 0), (-1, 0), (0, 1), (0, -1)] minutes 0 while fresh and q: r, c, t q.popleft() for dr, dc in dirs: nr, nc r dr, c dc if 0 nr m and 0 nc n and grid[nr][nc] 1: grid[nr][nc] 2 fresh - 1 q.append((nr, nc, t 1)) minutes t 1 return -1 if fresh else minutes代码里有个关键优化直接把新鲜橘子改成 2既标记了访问又防止重复入队。两个边界必须背熟初始没有新鲜橘子返回 0初始没有烂橘子但还有新鲜橘子返回 -1。岛屿数量这类题是连通分量计数DFS 和 BFS 都能做如果递归深度不够就用栈模拟或者 BFS。3.5 动态规划与贪心状态定义永远比公式重要动态规划在热题 100 里占比不低。我不建议一开始就啃难题而是从爬楼梯开始建立状态意识f[n] 表示到第 n 级台阶的走法转移方程是 f[n] f[n-1] f[n-2]。然后是打家劫舍dp[i] 表示偷到第 i 个房屋时的最大金额分偷和不偷两种决策转移方程是 dp[i] max(dp[i-2] nums[i], dp[i-1])。再到最长递增子序列dp[i] 表示以第 i 个元素结尾的最长递增子序列长度转移时要遍历前面所有比它小的元素。我见过太多人一拿 DP 就套模板反而忽略了最重要的一步状态定义。状态定义得好转移方程常常自然涌现状态定义模糊后面所有优化都是空中楼阁。如果你做 DP 题卡住请先在纸上把状态定义写完整这个 dp[i] 到底表示什么取值范围是什么边界条件是什么。写清楚之前不要急着写代码。贪心和 DP 的区别也经常被搞混。贪心只维护一个当前最优DP 维护一组可行状态。跳跃游戏、买卖股票的最佳时机 II 都是贪心经典题特征是每一步的局部最优累计起来就是全局最优。反过来一旦你发现需要保存多个方案进行比较大概率就不是贪心而是 DP。4. 我在实刷热题 100 时踩过的坑4.1 看题解一时爽合上题解全忘光这是最常见的一个坑不只是新手会犯我在第二轮刷题时也栽过。热题 100 的题解区有很多高质量文章读的时候无比顺畅合上屏幕自己写却到处卡壳。原因很简单看懂解法是消费别人的思路复现解法才是生产自己的思路。我给自己定了规矩一道题如果卡了 20 分钟还没有清晰思路可以看题解但看完必须关掉题解独立把代码写出来并用自己的话把思路讲一遍第二天再重做一次。没有这个动作刷 100 题等于搬运 100 次答案。这个复写动作之所以有效是因为它强迫你经历“从想法到代码”的完整转换过程。你卡住的点往往不是大方向而是细节比如边界怎么缩、去重怎么放。这些细节只有在独立写代码时才会暴露。4.2 只刷舒适区越刷越偏科第二个坑是无形中的。很多人会不知不觉把所有时间花在自己熟悉的专题上因为 AC 的数量让人上瘾。结果 100 题过完列表里数组题全绿动态规划全灰。这样刷完面试时一考短板立刻露馅。我的解决办法是画能力矩阵每周统计一次每个专题的通过题数和平均用时如果某个专题通过数明显落后就强制安排两个晚上专攻它。怕 DP就先把爬楼梯和打家劫舍这类题反复拆开从暴力递归到记忆化再到递推把同一道题写出三种版本。怕图就先把腐烂的橘子这类多源 BFS 拆开揉碎直到自己能不看代码画出搜索过程。短板补起来之后你再回头刷熟题的速度不会变慢反而因为理解了底层原理更容易融合贯通。4.3 边界条件和性能陷阱面试扣分的重灾区LeetCode 的判题系统会给你齐全的测试用例但面试手写代码时没有提示很多隐藏错误都来自边界。我专门整理了一个自检表写代码前快速过一遍场景需要检查的点空输入数组先判断 len 0字符串先判断是否为空单元素左闭右开区间、循环结束条件重复元素双指针去重的移动逻辑目标不存在返回 -1 还是返回插入位置整数溢出用减法代替大数比较必要时用长整型递归深度Python 默认约 1000链状树会直接 RecursionError性能陷阱方面Python 写递归 DFS 必须小心。一棵单链二叉树就能让递归深度过千直接报错。这不是算法错了而是语言环境限制。遇到这种题优先改成栈模拟或者视情况调整递归上限但我更推荐前者因为迭代法更可控。4.4 周赛 430 这类限时赛怎么用才不浪费如果你时间允许每周参加一次 LeetCode 周赛是很好的补充。它的价值在于把热题 100 中零散的知识点随机重新组合并强制你在一个多小时里完成。你会在这里暴露几个平时看不到的问题切换题型时是否会被绕晕第三题卡住后心态是不是会崩罚时机制会不会逼你陷入反复提交恶性循环。我的建议是周赛只当检验工具不当学习工具不拼排名。前三题做完后第四题如果 15 分钟还没有明确思路果断放弃。赛后把每道题当作新题重新写一遍而不是直接看别人代码改一改。这个“复盘但不逐字对照”的过程比多做十道新题都管用。5. 三轮刷题法具体怎么排5.1 第一轮标签巡航先求 AC 不求最优第一轮我称为标签巡航。把 100 道题按专题分组每周专注一到两个专题。我实际走下来比较顺的顺序是数组、哈希表、双指针、二分查找、滑动窗口、链表、栈、二叉树、DFS/BFS、回溯、动态规划、图、贪心。这条链路的设计逻辑是先用最简单的数据结构建立手感然后接触双指针和二分这两种基础算法再进入对递归要求更高的树与图最后挑战状态抽象要求最高的动态规划。每天 2 题周末 4 题。第一轮的目标很纯粹能做出来就做做不出来就看题解看完题解必须复写。不要求时间不要求最优解只要求把 100 道题全部过一遍并留下记录。通常三周左右你会感觉到明显进步这在很大程度上可以视为见过套路之后的条件反射。5.2 第二轮乱序复刷一题多解第二轮是乱序复刷。把收藏夹里的题目顺序打乱不再按专题提示来。这一步的关键点是训练模式识别面对一道没有分类标签的题你能不能在一分钟内想起它对应哪个专题套路。我每天随机抽 5 道题要求每道题从读题到 AC 控制在 30 分钟以内如果半小时做不出来就翻上一轮的笔记先看错误点再继续想 10 分钟。如果连续抽到两道同专题的题都卡住了说明第一轮对该专题的掌握还不够扎实需要回头复习。第二轮我还会尝试一题多解。每道题至少写出第二种解法递归改迭代DFS 改 BFSDP 改记忆化搜索。这不只是炫技它能让你想明白不同解法的空间时间权衡。比如二叉树遍历递归写法简洁但有递归深度隐患迭代法多一个栈但稳定面试聊起来就是加分项。5.3 第三轮模拟面试模式练出手感也练出嘴感第三轮模拟面试模式。热题 100 这时候已经不是一个刷题清单而是你的面试题库。每天给自己安排一场 45 分钟的模拟一道 Medium 题白纸手写代码写完后口头说复杂度。我建议照着这个模板讲先确认输入和边界再给主思路然后说复杂度最后走一个示例。比如“我的思路是先排序再用双指针从两端逼近因为数组是升序的移动哪一边取决于当前和与目标的关系。时间复杂度排序 O(nlogn)、双指针 O(n)总时间 O(nlogn)空间 O(1)。”听起来很简单但真正能在压力下流畅说出来的人不多。这轮有一个很加分的习惯学会主动说暴力解。如果最优解一时没想起来先快速给一个暴力解再补一句“如果内存允许我可以进一步优化”。这比沉默十秒强得多。暴力解至少证明你的基础扎实而且很多面试官会顺着这个思路引导你优化反而聊得更顺畅。6. 高频问题速查6.1 刷到什么程度算过关我给自己定义的标准不在刷题轮数而在三个表现。第一看到一道题能在 10 秒内说出它属于哪个专题以及基础解法是什么第二中等题 30 分钟内 AC简单题 15 分钟内 AC第三能把解法讲给别人听而不只是自己能写出来。三条都满足热题 100 就可以算吃透了。这时候哪怕再遇到新题你也会有一个明确的解题启动流程。6.2 做不出来时先看题解还是先硬扛我的原则很固定前 10 分钟只读题、想数据范围、判断专题第 10 到 20 分钟尝试写暴力解或简化版20 分钟后如果还没有实质进展就标记为需要啃题解但看完题解必须当天独立复写一遍。不要觉得看题解丢人真正丢人的是同一个题解看了十次合上屏幕还是写不出来。第一轮靠题解推进不可耻第二轮还靠题解才需要警惕。6.3 吃透 100 题之后下一步补什么如果时间充足我建议往三个方向扩展。第一高频变形题比如腐烂的橘子一变就可以变成多源扩散加最短路径组合的场景第二热题 100 没有完全展开的数据结构比如 Trie、并查集、LRU 缓存的变体第三字符串类经典算法KMP、前缀函数这类内容虽难但大厂面试出现率并不低。扩题的原则是先扩热题 100 的高频邻居而不是随机进题库乱刷。6.4 面试现场的几个细节面试现场有几个细节值得反复提醒。拿到题目先确认输入条件和边界比如数组是否有序、是否包含重复值、整数范围会不会溢出然后清晰地说思路和复杂度再动手写代码。写代码时变量名不要用 a、b、c 敷衍哪怕是一个临时变量也用 left、right、sum、pos 这种能一眼看懂的名字。代码写完把示例输入代入走一遍很多低级错误当场就能被发现。热题 100 刷透了面试现场大概率不会遇到完全陌生的题区别只在于你能不能把学到的套路在压力下稳定地施展出来。我在实际刷完这些题目后最大的感受是算法面试最公平的地方在于它不看资历只看出手。热题 100 带给你的不是“看过这道题”的侥幸而是一种“我知道该往哪个方向想”的稳定感。最后分享一个小技巧每当你做完一道 Medium 题试着把题目的限制条件反过来想一遍比如去掉排序、加上负数、把数据量放大到 10^9。你会发现很多 Hard 题就是这样长出来的。把这个习惯坚持到第二轮你对题目的理解会比单纯刷题深刻得多。
返回列表