ARTICLE DETAIL

资讯详情

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

LeetCode Hot 100刷题攻略:分类集训与模板复盘,拿下算法面试

LeetCode Hot 100刷题攻略:分类集训与模板复盘,拿下算法面试 我不打算再堆一遍“Hot 100是什么”这类你搜一下就知道的废话。直接说结论LeetCode Hot 100这份题单是目前算法面试高频题最浓缩的一份清单。前50题基本是基础套路大礼包后50题开始出现综合性设计。我这两年带人刷题不管对方是科班还是转码用的都是同一套打法先把题单按数据结构切块再用固定模板吃透每一类最后用复盘把“卡住的地方”变成自己的直觉。这篇文章我就按这个顺序完整拆一遍环境、套路、计划、排坑都会讲到。1. 先看懂Hot 100这是刷题计划表不是题库很多人上来就在LeetCode页面点开Hot 100从第1题“两数之和”开始按顺序刷。这是个极其常见的误区。Hot 100并不是按照难度递增排列的如果你真的从1到100顺着刷大概率会在第30题左右被各种综合题打得怀疑人生然后弃坑。1.1 题单的整体结构前50题练套路后50题考综合Hot 100的题目来源是大量真实笔试、面试的高频考题统计所以它天然带着“考点权重”。我把它粗略分成两段前50题数组、链表、字符串、哈希、双指针、滑动窗口、基础动态规划、基础回溯。每一道题背后都是一个可以迁移的套路难度集中在“中等”适合建立肌肉记忆。后50题二叉树、图、堆、前缀树、进阶DP编辑距离、正则表达式匹配、设计型题目LRU缓存、前缀树实现、用栈实现队列。这些题往往要叠加两到三个基础技巧或者查考你对数据结构本身的理解。所以我的建议是不要从1刷到100先把题目按“数组/字符串”“链表”“树”“图”“动态规划”“回溯”“堆/栈/队列”分类再从每类里挑3到5道经典题连续打通。连续练同一个套路比一天换一个题型效率高太多。1.2 为什么偏偏要选Python来刷用Python刷Hot 100最核心的原因是表达效率。算法面试本质是考察你“能否在半小时内把模糊思路变成可运行代码”。Python的语法非常接近伪代码能让你把大脑算力集中在逻辑设计上而不是花在指针类型、内存释放这些细节上。比如统计频率用collections.Counter键值对默认值用defaultdictBFS队列用deque这些C要写一大堆的活Python一行搞定。但这里必须说清楚一个反向代价Python的执行效率远低于C/Java在LeetCode的极限用例下更容易超时。所以你不能依赖Python的“高级特性”去作弊。比如有人用切片翻转字符串、用all()找出所有组合来暴力过关这样刷题是刷了个寂寞。你得主动给自己加限制能用O(n)的不要写O(n²)该剪枝的一定要剪枝。面试官也不是傻子他们会追问复杂度你答不上来代码过了也没用。2. 刷题前的环境准备装对Python配好调试环境刷题这件事环境问题比算法问题更容易劝退新手。我见过太多人最后不是卡在题目上而是卡在“明明装了Pythonvscode却跑不起来”。这一节先把环境彻底理清楚。2.1 Python安装与vscode配置里最常见的坑Windows安装Python时最容易踩的坑是漏掉Add Python to PATH。安装包打开后第一屏就有这个勾选项默认是不勾的你不手动勾上装完在终端输python就是“不是内部或外部命令”。macOS和Linux用户则要注意权限问题建议从官网下载安装包不要用系统自带的旧版本导致语法特性跟不上。装完Python之后vscode里还要装一个Python扩展插件。装完插件后再按CtrlShiftP输入Python: Select Interpreter选到你刚装的解释器路径。很多人没做这一步导致vscode不知道你用的是哪个环境import numpy这种命令报错或者完全没有智能补全。这个操作每个新环境都要做一次不是装一次就一劳永逸。另一个高频坑是环境管理混乱。我见过有人电脑上同时装了官网Python、Anaconda、Microsoft Store版Python三个环境版本各不相同。你用终端输pip装的库装到的是系统Python而vscode里跑的是Anaconda的虚拟环境import自然失败。解决思路很简单先用where pythonWindows或which pythonmacOS/Linux确认当前用的是哪个环境再想清楚你到底要让哪个环境承载刷题。如果只是刷题一个干净的环境完全够用别把自己搞成运维。2.2 最值得记住的几个标准库Hot 100里Python能发挥最大威力的标准库就几个按我自己的使用频率排序collections.Counter统计频率、判断互为字母异位词一行搞定。collections.defaultdict建邻接矩阵、构建图省去“key不存在先初始化”的样板代码。collections.dequeBFS和滑动窗口的御用队列双向操作效率高。functools.lru_cache递归函数上面加一个装饰器立刻获得记忆化效果普通DFS瞬间变成DP。heapqTop K问题、合并K个有序链表、找中位数堆操作全用它。bisect有序数组定位插入位置二分查找问题节省大量手写代码。用这些库不叫投机取巧它们本身就是标准库面试官认可。但问题是你要说得清复杂度。打个比方你在写题时用heapq.nlargest(k, nums)看起来一行就解决了Top K但面试官追问“这个函数的复杂度是多少”你要是答不上来那就是这次面试的红灯。用库的前提是“你不用库的大白话写法也能手写出来”。2.3 本地调试骨架让每道题都有回归用例我强烈建议不要只在LeetCode网页IDE里写题。网页IDE提交方便但没有断点、没有变量观察窗排查边界条件效率极低。我的做法是在本地vscode里维护一个模板文件每个题目都转成一个可运行脚本开头带一组自测用例。比如最经典的两数之和from typing import List def two_sum(nums: List[int], target: int) - List[int]: seen {} for i, num in enumerate(nums): if target - num in seen: return [seen[target - num], i] seen[num] i return [] if __name__ __main__: test_cases [ ([2, 7, 11, 15], 9, [0, 1]), ([3, 2, 4], 6, [1, 2]), ([3, 3], 6, [0, 1]), ] for nums, target, expected in test_cases: res two_sum(nums, target) print(res, OK if res expected else fFAIL, expected {expected})每次改完逻辑跑一遍这个文件所有用例立刻验证。这一套流程看着简单但能让你的刷题效率翻倍。如果出错直接用断点或者print(i, left, right)这种关键变量打印几秒钟就能定位问题不用在网页上一次次提交浪费机会。3. 按套路拆解Hot 100四类高频题型一次打通Hot 100最让我喜欢的一点是里面的题目套路高度重复。你刷到后面会发现所谓新题不过是旧模板换了层壳。下面我按四类核心题型把最实用的套路拆开讲。3.1 数组与指针题哈希、双指针、滑动窗口连招数组和字符串是Hot 100的开篇主力几乎必考三类技巧哈希、双指针、滑动窗口。这三者是同一个思想的不同变种用较少的遍历次数换取额外的空间或已知信息。先说哈希。两数之和是最经典的入门题核心思路是“边遍历边存边存边找”每拿到一个数字看哈希表里有没有它要的“另一半”有就返回没有就把它自己存进表里。这样只用一次遍历时间复杂度O(n)。同思路的题还有字母异位词分组、最长连续序列。双指针则用在有序或“数组两端”的问题上。典型题是三数之和先排序固定一个数剩下两个数用左右指针从两端向中间走因为有序就能用“大了左移、小了右移”的方式逼近目标。去重是这道题的核心难点一定别忘了跳过重复元素。滑动窗口是处理“连续子串/子数组”问题的神器。理解的要点是右指针负责“扩展窗口”左指针在条件不满足时“收缩窗口”。以无重复字符的最长子串为例def length_of_longest_substring(s: str) - int: seen set() left 0 ans 0 for right, ch in enumerate(s): while ch in seen: seen.remove(s[left]) left 1 seen.add(ch) ans max(ans, right - left 1) return ans这个模板背下来后面像最小覆盖子串、水果成篮、找到字符串中所有字母异位词全部可以套同一骨架。区别只在于“窗口内什么时候满足条件”这个判断逻辑不同。3.2 链表与树递归与迭代的平衡链表题在Hot 100里占块头不小反转链表、环形链表、合并两个有序链表、相交链表、回文链表、LRU缓存。链表的麻烦之处在于指针操作容易写乱但核心动作其实就一组缓存后继、改向、移动。以最经典的反转链表为例迭代写法的骨架是三个指针def reverse_list(head): prev, cur None, head while cur: next_node cur.next # 先缓存后继 cur.next prev # 指向前驱 prev cur # 前驱前移 cur next_node # 当前前移 return prev树的题目则围绕递归展开因为树本身就是天然的递归结构。前序、中序、后序遍历的差别只是“访问当前节点”的位置不同层序遍历必须用队列核心操作是“一次取完当前层的所有节点”。套路非常固定处理当前节点递归处理左子树递归处理右子树。唯一容易忽略的是空节点判断和返回值设计先想清楚“空的时候返回什么”、“递归结果怎么向上传”。3.3 动态规划别背公式先学会四步推导动态规划是Hot 100里最难啃也最重要的一块。很多人一上来就看状态转移方程然后一头雾水。正确的姿势是先举一个小例子手动推一遍再总结出“每个位置怎么从前面的位置算出来”最后才写代码。我用的四步法是定义状态dp[i]表示什么必须一句话说清说不清就是还没想明白。写递推关系dp[i]和前面的状态怎么关联初始化数组的起始状态是什么确定遍历顺序是从左到右、从右到左还是二维的按行按列以最大子数组和为例一句口诀是“到当前位置为止的连续子数组最大和要么是前面的最大和加上当前数要么是当前数自己重新开一局”def max_subarray(nums): dp nums[:] # 初始化为原数组 for i in range(1, len(nums)): dp[i] max(dp[i - 1] nums[i], nums[i]) return max(dp)这里的dp数组还可以优化成两个变量滚动更新空间复杂度从O(n)降到O(1)。面试时主动提到这个优化是很加分的细节。爬楼梯、打家劫舍都是这个模式。编辑距离、最长回文子串则是二维DP递推关系写起来更复杂但步骤完全一致。坚持用四步法别一上来就背方程DP才能真正变成你自己的技能。3.4 图与回溯DFS“感染”法和回溯模板图论和回溯题在Hot 100里绝不缺席尤其是岛屿数量、全排列、组合总和、括号生成、单词搜索、课程表。回溯的模板几乎可以套所有“求所以可能解”的问题def backtrack(path, choices): if 满足结束条件: ans.append(path[:]) # 注意拷贝防止后续修改影响结果 return for c in choices: if c 不合法: continue path.append(c) # 做选择 backtrack(path, choices) path.pop() # 撤销选择全排列是回溯最标准的例证难在加一个visited标记已用数字。组合总和则是排序剪枝递归前先把候选排序一旦当前和超过目标就提前返回。括号生成则利用“左括号数必须大于右括号数”这个约束剪枝。岛屿类的图题有个非常经典的“感染”技巧遍历到一块“陆地”时计数加一然后用DFS或BFS把相邻的所有陆地都改成“水”。这样下次遍历就不会重复数def num_islands(grid): def dfs(i, j): if i 0 or i len(grid) or j 0 or j len(grid[0]) or grid[i][j] ! 1: return grid[i][j] 0 # 感染标记为已访问 for di, dj in ((1,0), (-1,0), (0,1), (0,-1)): dfs(i di, j dj) count 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] 1: count 1 dfs(i, j) return countBFS则用来处理“最短路径”类问题比如单词接龙。朴素BFS是从起点一步步扩展走完整个搜索空间双向BFS则是同时从起点和终点向中间扩展能大幅缩小搜索空间。代码写起来更复杂一些但在图上大时收益非常明显。4. 这样定刷题计划才不会三天打鱼两天晒网再好的题单没有节奏也刷不完。刷Hot 100这件事真正难的不是题目而是“持续”。我推荐一个三轮刷题法每一轮的目的不同难度也不同。4.1 三轮刷题法分类集训、限时盲打、口述思路第一轮分类集训持续3到4周。先按数据结构分类每天只刷同一类题型。比如这一周专攻数组双指针下周专攻链表再下周动态规划。每天一到两道“中等”难度题即可重点是把模板打熟。这个阶段不要碰难题否则容易产生挫败感。第二轮随机盲打持续2到3周。题型混合着来模拟面试限时45分钟一道。写不出来就老实承认去看题解然后当场复盘。这轮的目的不是“做出多少题”而是让大脑适应“解不出题时的压力”。很多时候面试挂不是不会做而是高压下脑子一片空白。第三轮口述思路持续1到2周。只看题目描述不打开编辑器先说出这题属于什么类型、用什么数据结构、最终时间复杂度是多少。说得出来再动手写代码。这个阶段你会发现自己经能识别出大部分题目的“骨架”看到题目自动联想到对应套路这就是真正质的飞跃。我建议的每周节奏大概是这样的阶段目标每日量第一轮熟悉套路建立肌肉记忆1~2道分类刷第二轮适应面试节奏训练抗压1道限时 复盘第三轮形成条件反射抓题目本质2~3道口述选1道写码4.2 复盘记录模板把“卡住的原因”变成码力刷题不复原等于白刷。这里的复盘不是让你把题解抄一遍而是记录“你卡在哪里”和“最优解的哪个一步戳破了你思维里的那层窗户纸”。我用的复盘模板是题目名 我的解法一句话 卡点哪一步卡住/想了多久 最优解的关键一步 时间复杂度我的 vs 最优 一句话教训举一个我最常见的例子。刷两数之和时很多人卡点是“下意识用两个嵌套循环想不到哈希存差值”。教训就记成一句话“看到‘找出两个数满足条件’第一时间想哈希。”这条教训两句话都算不上但当你刷到最小的k个数、连续子数组和这种变体题时这句话会第一时间弹出来指导思路。用Excel或者Notion拉一张表坚持记录三四周你会发现同一类坑你不会踩第二次。5. 我踩过的坑直接给你一份排查清单刷题过程中的报错、超时、内存溢出很多和算法无关而是环境或编码习惯的问题。这一节总结我见过最多、也最容易被“新手下病根”的坑。5.1 环境、依赖和PATH的连锁反应Python环境问题最典型的三个表现命令行输python没反应、import numpy报ModuleNotFoundError、vscode运行代码时解释器环境不对。这些问题的根源几乎都是“没有先确认哪个Python在执行”。排查顺序应该固定为先在终端输where python/which python确认当前解释器路径再确认vscode里选到的解释器跟终端一致最后在解释器里跑pip list看包到底装没装上。很多人因为电脑上多个Python版本并存导致“装了numpy却到处都找不到”本质上就是这个顺序没理清。遇到pip install超时或者下载特别慢的情况换成国内镜像源指定-i参数就能解决核心库安装问题90%都是环境路径问题不是代码问题。5.2 超时、内存溢出与边界错误的定位LeetCode报Time Limit Exceeded最可能的原因有三个按频率排序嵌套循环做了无意义的重复计算典型表现是复杂度O(n²)硬扛大数据量递归没有记忆化同一个子问题反复计算指数爆炸循环里做了复制、切片、字符串拼接之类的低效操作。遇到TLE时先别急着优化细节先问自己一句这个算法本身的复杂度是不是最优如果不是先换算法再去微调。Memory Limit Exceeded则多发生在几个场景一是二维DP矩阵内存超出了题目限制这时应该考虑滚动数组二是递归深度太深造成栈溢出RecursionError报错很明显可以用sys.setrecursionlimit()临时解决但根本办法还是转成迭代或用循环。还有一类容易被忽略的递归函数里的可变默认参数。比如def dfs(path[])这种写法多个递归路径会共享同一个列表行为完全不可控。正确做法是在函数内部用局部变量初始化。边界错误则几乎全部来自三种情况空输入没考虑、下标从0还是从1没想清楚、初始化值不对。我每次提交前都会用一个自查清单过一遍空值、单元素、全相同、全逆序、长度很长、数字很大。按这个清单逐一验证能拦下一大半的边界问题。5.3 调试习惯和几个容易写错的细节调试时我建议用“二分定位法”先把报错分成“逻辑错”和“边界错”两类再用打印或断点缩小到具体循环。打印中间变量时格式一定要带上下文比如print(i, left, right, cur_sum)比一个裸print(res)更容易看出变量间的关系。你可以在本地编辑器里加断点看执行流程比在刷题平台上反复提交给评测系统强太多。一个特别容易被忽略的细节是“结果需要拷贝”。回溯模板里记录答案时如果直接把path塞进结果列表后续path.pop()会把已经记录的答案一起改掉。必须写成ans.append(path[:])对Python新手而言这是第101个坑。还有一个细节是关于调试原则不要在网页上心里默写一遍就盲目提交先用本地的多用例骨架跑一遍跑通了再贴到网页提交能省下一大堆提交上限。LeetCode编辑器本身没有“本地运行”按钮所以本地这套骨架的价值就在这里。我个人带人刷完几轮Hot 100之后最大的体会是题单本身不重要重要的是你通过它获得的“解题直觉”。题目千变万化但套路始终就那几十个无非是哈希、双指针、滑动窗口、递归、回溯、DP、BFS这些组合。你真正刷完一遍之后再遇到新题会自动在脑子里把它归类然后从对应模板里挑一个往里面套。这种条件反射不是靠背题得来的是靠“连续几天刷同一个套路 每天记一条卡点教训”攒出来的。最后给一个小建议不要追求一天刷十道题。保持每天一到两道的稳定节奏配合你那套复盘表格两个月后回头你会发现自己写题的速度和底气完全不一样了。
返回列表