☰
LeetCode热题100高效刷题指南:题型分布与三遍刷题法
2026/10/14 3:41:42 网站建设 项目流程

简介:这是一份以 LeetCode 热题 HOT 100 为主线的 C++ 题解 PDF,面向正在准备算法面试、希望系统刷题的开发者。资料聚焦高频题目的解法归纳,涵盖两数之和、两数相加、无重复字符的最长子串、寻找两个有序数组的中位数等经典问题,并从哈希表优化、链表进位处理、滑动窗口、二分查找等角度给出核心思路与 C++ 实现代码。每个题解均附有时间与空间复杂度说明,便于读者在面试中讲清思路与优化方向。包体为单个 PDF 文件,大小约 640KB,轻量易携带,可直接在电脑或移动端阅读。目前已有 1652 人学习使用,适合在笔试面试前集中过一遍高频考点,也可作为日常刷题时的查漏补缺参考。

1. 热题100是什么:为什么刷题的人都在啃这100道

某开发者第一次打开 LeetCode 热题 100 时,觉得“题目加答案”这六个字就是救星:题单列好了,题解翻一翻,标记个“已做”,一周刷掉三十道。第七天回头复习,发现连“两数之和”的哈希写法都要想半天——不是不努力,是打法错了。热题 100 是力扣按收藏、赞、讨论热度筛出来的一套高频题单,它最宝贵的不是“100 道题答案”,而是把算法面试的考点浓缩成了一张题型地图。这篇文章要解决的,就是怎么把这套题单从“刷过”变成“真会”,新手能跟着走,熟手能重新校准自己的题库。

2. 热题100的题型分布:先看清考什么,再决定怎么刷

2.1 十类题型占比:一份可以照着分配的清单

热题 100 不是随机凑的 100 道,它的分布和面试考点高度重合。按大类拆开,大概是这么个构成:

类别在热题100中的占比代表性题目(题号)面试权重建议优先级
哈希表与数组约 20%1 两数之和、49 字母异位词分组、128 最长连续序列高第一批
二叉树与递归约 17%94 中序遍历、102 层序遍历、236 最近公共祖先高第一批
动态规划约 15%53 最大子数组和、72 编辑距离、322 零钱兑换高第一批
双指针与滑动窗口约 12%3 无重复字符最长子串、15 三数之和、42 接雨水高第二批
链表约 10%206 反转链表、21 合并有序链表、146 LRU缓存中高第二批
回溯与 DFS约 10%46 全排列、78 子集、39 组合总和中高第二批
栈与单调栈约 6%20 有效括号、739 每日温度、394 字符串解码中第三批
堆与优先队列约 5%215 数组中第K大、347 前K个高频元素中第三批
图论与并查集约 5%200 岛屿数量、207 课程表中低第三批
其他(贪心、位运算等)约 5%338 比特位计数、763 划分字母区间低按兴趣

这张表的用法不是“按占比从大到小刷完”,而是帮你分配精力:时间只剩两周,舍掉图论和位运算的难题,把哈希、二叉树、动态规划、双指针这四块吃透,覆盖的面试面就已经很可观了。时间充裕,再按第二批、第三批铺开。

2.2 为什么这100道值得刷:题根逻辑

热题 100 里的题,很多是“题根”。所谓题根,就是一道题能演变出一串同族题。举三个最常见的链条:

  • 两数之和(1)是题根,加上排序和去重就变成三数之和(15),再继续套一层循环就是四数之和。面试官说“随便改改”,改的往往是题根上的约束,不是解法框架。
  • 最大子数组和(53)是题根,把数组首尾连起来就变成环形子数组最大和,加上“允许删除一个元素”就变成更难的变体。动态规划的状态定义方式不变,变的只是边界条件。
  • 反转链表(206)是题根,K 个一组翻转链表、回文链表判断都要用到“局部反转”这个操作。链表题不熟的时候,先做反转链表比做十道链表题都管用。

热题 100 的“热”不是刷出来的,是大量面试反馈和讨论沉淀出来的。一套题单里能抽出这么多题根,说明它的选取逻辑是“考频优先、前置优先”,不是按难度堆题目。把题根吃透,相当于把面试题库的地基先打了一遍。

2.3 不同人群的分配策略

同样是这 100 道,三类人的刷法完全不一样:

  • 新手(百题量以下):按上表的优先级分三批刷,前两批 60 道做到“合上书能复述解法”,后面 40 道只求“看过思路,能讲出大概”。不要追求全部 AC,追求的是题型见过。
  • 熟手(两百题以上但体系乱):不要从头刷,只挑自己做标记的题重刷,重点看“这题属于哪个题型变体”,把热题 100 当作自测清单用。
  • 突击型(三天后面试):只刷第一批的 45 道,外加链表全家族。每题只看最优解思路,不动手,用嘴讲一遍解法,效率最高。

提示:热题 100 的题单会小幅更新,但核心题根一直稳定。与其纠结题号有没有变,不如盯紧题型分布表。

3. 把“题目加答案”用起来:三遍刷题法与题解的正确读法

3.1 第一遍:45分钟原则和题解的正确打开方式

拿到一道题,常见做法是先翻开题解看答案再自己写一遍。我一般不建议这么干,因为看答案写代码是一种“舒适区编码”,很像抄作业,抄完觉得全会,合上全部忘掉。第一遍的正确姿势是限时盲刷:

  1. 打开题目,计时 45 分钟。
  2. 前 15 分钟只准想,不准写代码;想清楚暴力解先写出来。
  3. 45 分钟到了没 AC,去看题解,但只看“思路”部分,把代码区域用编辑器遮挡住。
  4. 看完思路后关掉题解,自己写代码。写不出来再看一次思路,写出来了就标记“解法不熟,隔天重刷”。

这一步的关键是“把默认答案藏起来”。热题 100 常见的配套资料都是“题目加答案”的形式,答案放在手边很容易顺手就点开;给自己设一道屏障,才能逼出真实的掌握度。动手前先把接口和边界条件写进备注,比如“数组为空返回什么”“目标值不存在返回什么”,这些追问在面试里比解法本身更致命。

第一次刷完,每道题记一行状态,格式随意,能看懂就行:

题号题目第一次状态解法关键词是否需要重刷
1两数之和看思路后 AC哈希表需要
15三数之和45分钟未AC排序+双指针+去重需要
53最大子数组和独立 ACDP、滚动变量不需要

这张表就是第二遍的起点。

3.2 第二遍:关掉题解,强制输出

第二遍的时间点放在第一遍后的 48 小时左右,只刷表格里标记“需要”的题。规则只有一条:开编辑器,关所有题解页面,从空白文件开始写。

这一步会被虐得很惨。明明昨天看懂了思路,今天可能连// 注释里要写什么都想不起来。这恰恰是正常的——前一天的“懂”发生在阅读语境里,今天的“写”发生在生产语境里,两者差了不只一个量级。A同学在第二遍自测时,连 15 题的双指针去重条件都写错了一个符号,查了半天才发现是nums[i] == nums[i - 1]写成nums[i] == nums[i + 1],当场标红这题。

第二遍的具体产出不是 AC,而是“解释”。每写完一道题,用三句话向自己解释:核心思路是什么、时间复杂度多少、边界条件有哪些。解释得清楚,说明这题真正入门了;解释得磕绊,回到第一步,再看一次题解思路,三天后重刷。

3.3 第三遍:随机抽查与讲解练习

第三遍的核心不是刷题,是防遗忘。热题 100 刷完一轮后,很多题会在一周内从大脑里蒸发,这不是记忆力问题,而是缺少随机提取训练。随机提取比按顺序重刷有效得多。

可以自己写一个极简的随机选题脚本,把已刷题号放进去:

import random hot100_finished = [1, 3, 15, 20, 42, 46, 53, 72, 78, 94, 102, 121, 136, 141, 146, 155, 198, 200, 206, 207, 215, 226, 236, 239, 283, 287, 297, 300, 309, 322, 337, 347, 394, 399, 406, 416, 437, 438, 448, 461] random.seed(2024) selected = random.sample(hot100_finished, 5) print("今日抽查题号:", selected)

这个脚本每次跑出 5 道题,做法是:看到题号后先别打开题目页面,凭记忆说出这题的“题型归属 + 核心解法 + 复杂度”,再去题解页核对。说得对就过,说不准就重新打开题,不用重写代码,把思路和关键代码默读一遍即可。参数说明一下:hot100_finished是你自己维护的已完成列表,按实际刷过的题号填入;random.seed(2024)保证调试时复现结果,实际使用可以删掉这一行,让每次抽查都不一样。

3.4 题解为什么要看“源码+复杂度”一起看

热题 100 的配套答案通常不止一种解法,常见的资料会把暴力解、优化解、最优解三个版本都放出来。很多人只把最优解复制提交,然后标记完成——这等于把整套资料里最有价值的部分扔了。最优解是“终点”,暴力解和优化解是“路线”,面试官问“你怎么想到的”,答的正是路线。

读题解的顺序建议是:先看暴力解,想清楚它慢在哪;再看优化解,看它去掉了哪部分重复计算;最后看最优解,确认时间复杂度是否能再降。以“接雨水”为例,暴力解对每个位置分别找左右最高墙,是 O(n²);单调栈优化到 O(n),但代码难写;双指针也是 O(n),代码更短。三条路线都看一遍,才算真正读完这道题的答案。

另外要警惕一个现象:题解里如果出现“官方题解”和“高赞题解”两种写法,优先理解高赞题解,因为它往往是用人话把官方题解翻译了一遍。官方解法偏严谨但啰嗦,评论区解法偏精炼但偶尔跳过边界处理。两个互补着看,比只看一个更稳。

4. 三道高频题的答案拆解:从暴力解到最优解的完整轨迹

4.1 两数之和:哈希表解法背后的动机链

两数之和是热题 100 的开门题,也是最优解思路最有代表性的一道。题目本身很直接:给定数组和一个目标值,返回两个下标,让这两个数相加等于目标值。

def two_sum(nums, target): seen = {} for i, num in enumerate(nums): need = target - num if need in seen: return [seen[need], i] seen[num] = i return []

逻辑说明:seen字典的键是“已经扫过的数值”,值是“这个值所在的下标”。每次遍历到一个新元素,先算它的配对需求need = target - num,如果need已经在seen里,说明之前遇到过它的另一半,直接返回两个下标。如果没遇到,把当前值和下标存进字典,继续向后扫描。

这里有两个关键参数要讲清楚:target - num是配对查询的键,不是下标;seen[num] = i的赋值顺序决定了下标不会重复使用。边界条件上,题目保证一定有且仅有一个解,所以遍历一定能 return;如果题目改成“可能无解”,函数末尾的return []就是兜底。这套思路的动机链是:暴力解每来一个数都要回头扫前面的数组找配对,O(n²);哈希表把“回头扫”变成了 O(1) 的字典查找,整体降到 O(n)。面试追问的“能不能一次遍历”,指的就是这个解法。

4.2 反转链表:迭代与递归的取舍

反转链表是链表家族的题根,很多人在面试时能写出迭代版,但一旦被问到“递归怎么写”就卡壳。两个版本都值得记在脑子里:

# 迭代版:逐个翻转指针方向 def reverse_list(head): prev = None curr = head while curr: nxt = curr.next # 先保存下一个节点,防止断链 curr.next = prev # 当前节点指向前一个节点 prev = curr # prev 前移 curr = nxt # curr 前移 return prev # 新头节点是原链表的尾节点 # 递归版:先走到尾,再往回改指针 def reverse_list_recursive(head): if head is None or head.next is None: return head new_head = reverse_list_recursive(head.next) head.next.next = head # 让下一个节点反过来指向当前节点 head.next = None # 当前节点的 next 置空,防止成环 return new_head

迭代版的关键是那个nxt = curr.next临时变量。反转链表的本质是“把当前节点的 next 指向前一个节点”,但这个操作会把原本的下一个节点弄丢,所以必须先用nxt存起来。递归版的思路则是把问题拆成“把 head 后面那一截反转好,再把 head 接到队尾”。

面试官通常追问两点:两个版本的复杂度和递归的风险。迭代是 O(1) 空间,递归是 O(n) 栈空间;链表长度到上万时,递归可能触发栈溢出。如果你能用迭代版 AC,然后用一句话说清递归版的区别,这道题在面试里就过关了。

4.3 接雨水:双指针解法的现场推演

接雨水在热题 100 里被很多人标成“难题”,实际它的双指针解法只有十几行。题目问的是:给定每个位置的高度数组,下雨后能接多少水。

def trap(height): left, right = 0, len(height) - 1 left_max = right_max = 0 ans = 0 while left < right: if height[left] < height[right]: if height[left] >= left_max: left_max = height[left] else: ans += left_max - height[left] left += 1 else: if height[right] >= right_max: right_max = height[right] else: ans += right_max - height[right] right -= 1 return ans

核心思想一句话:每个位置能存的水量,取决于“左侧最高墙”和“右侧最高墙”中较矮的那一侧。所以两个指针从数组两端往中间走,哪边矮就先处理哪边,边走边维护左半区已扫过的最高墙left_max和右半区的最高墙right_max。

注意height[left] < height[right]这个分支判断:它保证当前处理的一侧left的存水量只受left_max约束,因为右侧至少有一堵不低于height[right]的墙兜底。同理,else分支处理右侧时,right_max左侧永远有更高的墙接住。两个临时变量left_max和right_max就是解题的题眼,它们不是全局最高,而是“当前指针移动路径上的最高值”。

接雨水的另一条常见路线是单调栈,核心逻辑是“按层计算”,遇到比栈顶高的柱子就出栈结算一次。双指针和单调栈的复杂度都是 O(n),双指针更短、容易讲;单调栈能顺带解决“柱状图中最大的矩形”,属于扩展收益更高的解法。备考时间有限就先把双指针版练熟,时间富余再补单调栈。

提示:热题 100 里类似“看着难、解法其实很短”的题还有不少,比如“只出现一次的数字”用异或、“多数元素”用投票,碰到这类题,优先理解为什么能这样做,再背代码不迟。

5. 刷热题100最常见的五个坑:从背答案失效到放弃治疗

5.1 把“题目加答案”当成背诵材料,一变形就翻车

现象:A同学把热题 100 的题解打印出来背了两遍,每道题能默写,但面试官把“两数之和”改成“三数之和最近值”之后,代码写了一半就写不下去,只记得哈希表,忘了去重逻辑从哪下手。

原因:背诵记忆的是“代码字符”,不是“解题结构”。字符序列在题目环境变化时无法迁移,只有“结构”(比如三数之和 = 排序 + 固定一个数 + 双指针找两数)才能迁移。把“题目加答案”当成教材而非题库,是这类翻车的根源。

解决:背书式刷题改成结构式刷题。每道题退一步问自己:这题的最优解把问题拆成了哪几步?比如两数之和是“查询历史值”,三数之和是“排序 + 迭代 + 双指针收敛”,记住的是操作,不是代码。每次变体题出现时,先对照操作结构,再写代码,能写对一半以上。

5.2 看题解秒懂,上手代码就卡住

现象:打开题解,从头读到尾,觉得每一步都合理,关掉页面自己写,却连第一行left, right = 0, len(nums) - 1的初始化都想不起来该用什么。这种现象在滑动窗口和双指针题里尤为常见。

原因:阅读答案时大脑处于被动接收状态,没有主动编码“为什么从这开始”。变量初始化的选择和边界条件,往往在题解的代码里被默认合理,但读者的注意力被文字解释带走,根本没有盯住代码的骨架。

解决:第二遍重刷时强制空写。不看题解先画一张“变量角色表”:left是什么、right是什么、循环不变式是什么。画得出来再写代码,画不出来就说明这题还没消化,标记三天后重来。这个方法多花五分钟,但能少踩三次同样的坑。

5.3 跳过复杂度分析,面试被追问就露怯

现象:做题时只看代码能不能 AC,从不算复杂度。面试时被问“你的解法时间复杂度和空间复杂度分别是多少”,答案变成“应该……大概是……”。如果不能准确说出,面试官会默认你对解法的理解停留在“能跑”层面。

原因:本地跑 AC 只能证明小数据量下逻辑正确,不能证明解法在大输入下依然可用。很多热题 100 的题解里都写了复杂度,但那一行通常在代码块下方,容易被扫过去。

解决:养成每题提交前先写两行注释的习惯:

# time: O(n) # space: O(n)

写错没关系,但要写出来。第二轮复习时把这些注释当答案背下来:两数之和是 O(n)/O(n)、反转链表迭代版是 O(n)/O(1)、接雨水双指针是 O(n)/O(1)。复杂度边界清楚了,面试追问就有的说。

5.4 只刷列表不画题型地图,临时换题直接懵

现象:热题 100 刷完一轮,发现“岛屿数量”和“课程表”都用到了“访问标记”这个技巧,但当时根本没意识到两者同源。面试时遇到一道“无向图找连通分量”,第一反应是没见过,而不是想到“这不就是岛屿数量换了个壳”。

原因:按列表顺序刷题时,大脑按“题号”归档知识,而面试按“题型”出题。题号索引在真实提问场景下无法快速定位,翻车是必然的。

解决:刷第二轮时建一个题型标签,一道题可以挂多个标签。比如“岛屿数量”挂“DFS/BFS/网格连通性”,“课程表”挂“拓扑排序/有向图”。一题多标签能把散点串成网,遇到新题先对标签,再对解法。某开发者的血泪经验是:把这个标签维护到五十题以后,新题识别速度会明显变快。

5.5 时间分配失衡:简单题不练、困难题死磕

现象:B同学刷热题 100 时,一上来就死磕“编辑距离”和“二叉树最大路径和”,每道题耗两小时,AC 后成就感很强;相反觉得“有效的括号”“移动零”太简单,看一遍就跳过。结果面试碰到“两数之和”的变体,因为代码写得不够快,反而丢了分。

原因:基础题的熟练度决定面试的“下限”,困难题的思路决定“上限”。很多面试的区间就在热题 100 里偏中等难度,把时间耗在困难题上是战略性失分。

解决:按 1 : 2 : 1 的比例分配时间,简单题练速度和边界,中等题练思路和代码,困难题只挑三类:题根类(如接雨水、编辑距离)、题型代表性强的(如 LRU 缓存)、面试高频提到的。属于“偏怪偏难”的题,看懂思路即可,不值得反复重刷。

6. 把热题100变成你的模板库:一个值得长期维护的刷题笔记习惯

6.1 题型模板的两种写法

热题 100 刷到后半段,你会发现同一套代码骨架反复出现。把这些骨架单独抽出来,比刷完就扔有用得多。以最经典的滑动窗口模板为例:

def sliding_window(s): n = len(s) window = {} left = 0 ans = 0 for right in range(n): # 1. 扩展右边界,更新窗口状态 c = s[right] window[c] = window.get(c, 0) + 1 # 2. 收缩左边界,直到满足题设条件 while not is_valid(window): window[s[left]] -= 1 if window[s[left]] == 0: del window[s[left]] left += 1 # 3. 更新答案 ans = max(ans, right - left + 1) return ans

这是骨架,不是可运行代码,is_valid(window)需要按题实现。拿到“无重复字符的最长子串”时,is_valid变成“窗口内没有重复字符”,也就是所有window[key]都等于 1;拿到“最小覆盖子串”时,is_valid变成“窗口内已包含目标串全部字符”。同一套骨架,换一个is_valid,就是两道题。把热题 100 里所有套这个骨架的题——无重复字符最长子串、最小覆盖子串、字符串排列——归成一组,比单刷十道题更划算。

6.2 一张“题型卡片”应该记录什么

我一般会为每个高频题型维护一张卡片,记录五个字段:题目列表、核心思路、复杂度、边界条件、变体方向。示例是“双指针类”的卡片内容:

字段内容
题目列表三数之和、接雨水、盛最多水的容器、移动零
核心思路左右指针同向或相向移动,空间换时间
空间复杂度双指针本身 O(1),排序开销另算
边界条件数组长度小于 2、指针相遇时的退出条件
变体方向三数之和加去重、接雨水换单调栈、容器题换数对

这五个字段不是一次写齐的,刷到新题时回头补一行就行。维护成本很低,但复习效率极高——回看一张卡片,等于过了一遍整个题型族,比重刷二十道题快得多。

6.3 每周维护节奏

把热题 100 变成模板库之后,保持一个很小的习惯:每周花二十分钟重翻一遍卡片,把印象深刻的新卡贴进去,把已经内化的旧卡降级。我在刷完热题 100 后的三个月里,每周只保留十张卡片,每张卡对应一个题型族,面试前看两个小时就能把状态找回来。这是我刷题路上最值的一个习惯——从“刷题”走向“能用题”,中间隔的不是题量,是整理。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询