简介:吴师兄学算法Leetcode精讲200题是一份面向算法初学者与准备笔试面试的编程学习者的PDF电子书。内容以“刷经典题而非题海”为核心理念,整理出205道精选LeetCode题目,并专门为入门者设计了循序渐进、由易到难的刷题顺序,每天投入两小时,两个月左右即可完成全列表,尤其适合新手小白转型、计算机基础薄弱者系统起步。资源为单个PDF文件,压缩包大小19.83MB,目前已有178人浏览学习。内容以动画视频讲解配合代码注释的形式呈现,弱化冗长文字,并附有参考网站与作者微信交流渠道,方便获取后续更新。除了算法方法论外,书中还覆盖Python与Java基础语法要点,并针对代表性题目如LeetCode 26题“删除有序数组中的重复项”给出Java、C++、Python三种语言的参考代码与指针思路拆解,帮助读者理解原地修改数组、去重等高频考点。整体而言,这份PDF将刷题顺序、语言基础、实战解析有机整合在一起,是一份把算法学习路线落到具体操作层面的实用资料。
1. 一份200题精讲PDF,凭什么比刷满1000题更接近真实面试水平
手边这份「吴师兄学算法Leetcode精讲200题.pdf」在网上流传了很久,很多人把它当作「又多了一份题解」收藏起来。我的看法不太一样:它的价值不在题量,而在「按题型收敛」这套思路。200道题按专题排开,每个专题只挑最高频的结构反复练,比按编号从头往下刷到800题更能建立面试需要的题型坐标系。大多数人的问题不是题刷得不够,而是题型覆盖不均、只刷不总结。这个方向适合两类人:一是刚准备求职、不知道从哪下手的初学者,二是刷过几百题但一进面试就发挥不稳的熟手。下面我按「先看懂地图、再动手执行、最后验证掌握」的顺序,把这套方法完整拆开。
2. 200题背后的算法地图:题型占比、专题顺序与资料定位
一份精讲资料值不值得投入,先看它的题型分布是否合理。如果200道题里二叉树占了80道、动态规划只有2道,那它更适合当补充材料,不能当主线。
2.1 题型占比:为什么数组和二叉树占了三分之一
按一线算法面试的高频分布,200道题的大致比例应该是这样:
| 专题 | 常见考查点 | 建议占比 |
|---|---|---|
| 数组与哈希 | 两数之和、前缀和、原地操作 | 约20% |
| 双指针与滑动窗口 | 有序数组、连续子串、链表环 | 约12% |
| 栈与队列 | 括号匹配、单调栈、层级遍历 | 约10% |
| 二叉树与递归 | 遍历、最近公共祖先、路径和 | 约15% |
| 回溯与DFS | 排列组合、岛屿数量、状态枚举 | 约10% |
| 动态规划 | 爬楼梯、背包、子序列 | 约15% |
| 贪心与排序 | 区间合并、会议安排 | 约8% |
| 图与拓扑 | 邻接表、环检测、拓扑排序 | 约5% |
| 其他 | 位运算、堆、二分查找 | 约5% |
这个比例不是某份文档的原文,而是我按刷题社区里常见的高频考点整理的通用分布。用它对照你手上的200题,能很快发现偏科。比如你手里的资料动态规划占比不到10%,就说明这套题的主线倾向「基础结构」,DP需要自己补充。反过来,如果DP占了三成,前期刷起来会非常吃力,不适合零基础起步。
为什么数组和二叉树加起来能到三分之一?因为数组是所有容器的最底层形态,哈希、双指针、前缀和全部建立在它上面;而二叉树是练习递归最好的载体,递归思想又是回溯、图、动态规划的公共基础。把这两个板块吃透,后面很多题其实是在复用同一套思维,只是包装不同。
2.2 精讲的价值不在答案,在「从暴力到最优的思路轨迹」
很多人看题解只看最终代码,这是最亏的用法。一份好的精讲题解,最有价值的部分是中间那段「为什么一开始想到暴力,然后从哪里发现可以优化」的推导过程。
以两数之和为例。暴力做法是两层循环,O(n^2)。优化的突破口在于:内层循环做的事情是「查找」,而查找一个值在不在集合里,正是哈希表最擅长的事情。于是外层遍历数组时,把遍历过的值放进哈希表,内层查找就从O(n)降到了O(1)。这个「把查找换成哈希」的思路,不只是这道题能用,后面遇到变位词分组、连续子数组求和、判定重复元素,全是同一招。
所以我在看精讲时有个习惯:先盖住解析,花5分钟写一版自己的解法,哪怕是暴力的,再打开解析逐行对比。对比的重点不是代码差异,而是「我的思考在哪一步断掉了」。这一步断掉的地方,就是这道题真正要学的东西。把这个断点记下来,比抄一遍完整代码有用得多。
2.3 按依赖顺序推进专题:数组→链表→栈队列→二叉树→回溯→动态规划
200道题不能按题目编号从头刷,要按专题依赖关系排顺序。我一般会这样安排:
- 数组与哈希最先,因为几乎所有题都依赖遍历和容器操作;
- 双指针紧接着数组,它解决的「有序数组两个端点互相逼近」依赖排序和下标操作;
- 栈与队列需要的容器概念在数组链表之后学,更顺手;
- 二叉树放在回溯之前。回溯本质上是「递归 + 撤销选择」,递归不熟练就去做回溯,很容易陷入死循环出不来的焦虑;
- 动态规划放在回溯之后。DP的很多状态转移,其实就是把回溯里的暴力搜索改成记忆化,再改成自底向上的递推;
- 图放在最后,因为邻接表的BFS/DFS可以看作「多叉树 + 回溯」的延伸。
按这个顺序推进,每一层新知识都能从旧知识里长出来,不会出现「这条路我完全没见过」的断裂感。反过来,如果有人一上来就刷动态规划,大概率前20题就把热情耗光了。
3. 用「三层刷题法」跑通200题:计划模板与代码框架
把200题拆成专题之后,每一道题具体怎么过?只看一遍答案没有任何用。我用的方式是三层递进:跟写、独立重写、限时模拟。
3.1 第一层「跟写」:先自己写10分钟,再对照精讲改错
第一层不是照抄,而是「带着自己的半成品去对答案」。看到一道题,先用10分钟在纸上写出输入、输出、约束,然后写一版自己的解法,哪怕暴力解也行。写不出来也没关系,这个思考过程会让后面看解析时更容易记住关键点。
以二分查找为例,这是200题里复用率最高的模板之一:
def lower_bound(nums, target): # 在有序数组 nums 中找到第一个 >= target 的下标 # 统一用左闭右开区间 [left, right),避免边界混乱 left, right = 0, len(nums) while left < right: mid = (left + right) // 2 if nums[mid] < target: left = mid + 1 # mid 太小,左边界右移 else: right = mid # mid 可能是答案,右边界收过来 return left逻辑说明:这段代码找的是「第一个不小于目标值的位置」,也就是lower_bound。核心不变量是:答案始终在左闭右开区间[left, right)里。当nums[mid]小于target时,mid位置不可能是答案,所以left直接跳到mid+1;否则mid可能是答案,right收到mid而不是mid-1,这样不会漏掉相等的情况。
参数说明:nums必须是升序数组,target是查找值;返回值可以直接当插入位置用,也能用来判断target是否存在——返回下标在长度范围内且nums[ret] == target才表示存在。常见误用是有人把right初始化为len(nums) - 1,然后用left <= right的写法,两种写法都可以,但不要混着用。选定一种并固定下来,比纠结哪种更好更重要。
3.2 第二层「独立重写」:合上解析,把卡点留在纸上
第一层学完的题,隔1到2天做第二层:关闭所有解析,在编辑器里独立重写一遍。这一步的目标不是「写对」,而是「暴露卡点」。
def length_of_longest_substring(s): # 经典滑动窗口:找出不含重复字符的最长子串长度 left = 0 # 窗口左边界 max_len = 0 seen = set() # 当前窗口内的字符集合 for right, ch in enumerate(s): # 遇到重复字符,从左边收缩窗口,直到把重复字符移出去 while ch in seen: seen.remove(s[left]) left += 1 seen.add(ch) max_len = max(max_len, right - left + 1) return max_len逻辑说明:外层for循环让right一步步向右扩展窗口,窗口内用set维护「当前无重复字符」的状态。每次加入新字符时,如果它已经在set里,说明窗口内出现了重复项,于是从左边逐个移除字符,直到把这个重复字符从窗口里清掉。每次操作后窗口都是无重复的,所以可以放心更新最长长度。
参数说明:s是输入字符串;left和right分别是窗口的左右边界,构成闭区间[left, right];seen里存的是当前窗口内出现的字符。需要注意:while循环移除的s[left]不一定是重复的那个字符,可能要先移除几个「无辜」字符才能到达重复位置,这是滑动窗口收缩的正常过程。如果这一步写得卡壳,多半是没想清楚「窗口不变量」是什么。
独立重写时,我会在代码注释里记录两个东西:一是卡在哪个位置超过5分钟,二是当时为什么想歪。这两行记录比代码本身更值钱,因为面试官问的「你当时怎么想到的」,答案就藏在这里。我见过太多人把这道题背下来,结果同样是滑动窗口的变式题一改条件就不知所措。
3.3 第三层「限时模拟」:用一页纸模板固定高频套路
前两层过完的题,进入第三层:限时模拟。简单题15分钟,中等题30分钟,困难题45分钟。到时间没写出来就停下,看解析,然后把这道题标记为「待二刷」。这个阶段同时做一件事:把某一类题归纳成一页纸模板。
from collections import deque def level_order(root): # 二叉树层序遍历,按层返回节点值的二维列表 if not root: return [] res = [] q = deque([root]) while q: level_size = len(q) # 先固定本层节点数,防止把下一层混进来 cur = [] for _ in range(level_size): node = q.popleft() cur.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) res.append(cur) return res逻辑说明:层序遍历的关键是「先进先出」,所以用队列。每次进入while循环时,队列里恰好是当前层的全部节点;用level_size先记录下来,再一次性弹出这一层,弹出的过程中把下一层的左右孩子追加到队尾。如果不固定level_size,循环会悄悄把下一层的节点也当成当前层处理,输出就会错乱。
参数说明:root是二叉树根节点;q是存放节点的双向队列;res是二维列表,每一层一行。这个模板还能迁移到「按层处理」的很多场景,比如求每层最大值、填充下一个右侧节点指针、找最底层最左节点,改的地方只是内层循环里对cur的处理逻辑,队列结构完全不用动。
一页纸模板不需要长,每个专题控制在15行以内,写清楚三件事:识别条件(这道题怎么看出该用这个套路)、复杂度、易错点。这些模板是后面章节要讲的「模板库」的核心素材。
3.4 一周计划模板:每周只推一个专题,不贪多
200题不是一个月能刷完的,按周推进更现实。我常用的计划是这样的:
| 时间 | 内容 | 题量 |
|---|---|---|
| 周一 | 新专题第一层「跟写」 | 4题 |
| 周二 | 新专题第一层「跟写」 | 4题 |
| 周三 | 对周一、周二的新题做第二层「独立重写」 | 6题 |
| 周四 | 新专题继续跟写 + 前一个专题补漏 | 4题 |
| 周五 | 本周全部新题做第二层 | 4题 |
| 周六 | 限时模拟:混合专题3题,各30分钟 | 3题 |
| 周日 | 整理模板库 + 重做本周卡壳的题 | 不定 |
这个节奏每周新题稳定在12道左右,但复习量比新题量还大。很多人刷了200题还是没进步,问题不在量,在复习比例太低。我见过有人一个月刷了80道新题,从不回头看旧题,面一次挂一次,不是不会做,是全都忘了。
提示:如果你每天只能投入一小时,就把周三和周五的独立重写当作底线,其他天的跟写可以少到每天2题。放弃跟写还能接受,放弃独立重写基本等于白刷。
4. 刷LeetCode最容易翻车的五个坑:现象、原因与排查路径
这章写的都是高频踩坑场景,覆盖了我自己和其他人反复出错的位置。每一条都按现象、原因、解决三步拆,方便你对照排查。
4.1 题解看得懂,合上资料就写不出来
现象:看精讲解析时每一行都懂,甚至觉得「这题不过如此」;合上PDF自己写,第一行就卡住,十几分钟憋不出一个函数签名。
原因:看题解是被动输入,大脑会产生「我会了」的错觉。实际做题是主动输出,需要自己完成「识别题型→选数据结构→写边界→验证」整条链路,链路里任何一环没练过都会断掉。
解决:把顺序调换成「先写后看」。拿到题先写10分钟,写不出再打开解析;打开解析只看到思路部分,不看代码,然后自己接着写。这一步可以把「看懂了」的虚假掌握感直接挤掉。刷题不是看谁收藏的题解多,是看谁能徒手写出来。
4.2 本地能跑,一提交就数组越界或空指针
现象:代码在自己电脑上跑示例通过,放到在线判题平台上下标越界、空指针,一堆红色错误。这是最常见的翻车现场。
原因:本地测试用例只有一两个,而且恰好是「正常路径」;判题系统会在输入里放空数组、单元素数组、最大值边界。这些边界情况你在写代码时根本没考虑。
解决:写代码之前,先在注释里写三个用例:空输入、最小规模、最大规模。比如数组题先问自己:「nums为空时第一个if能不能兜住?nums只有一个元素时循环里会不会越界?」然后把这三个用例在本地跑一遍再提交。这个习惯能挡掉八成提交报错。
4.3 模板默写成功,换个问法就不会
现象:二分查找模板背得滚瓜烂熟,面试官把「有序数组找目标值」改成「旋转后找最小值」、把「找目标值」改成「找第一个大于目标值的位置」,就直接卡住。
原因:背的是代码,不是「这个模板解决什么问题」。二分查找不是一段代码,是一种「每次把搜索空间减半」的思维。题目包装变一下,识别条件就失效了,自然无从下手。
解决:给每个模板写一句「触发条件」,贴在模板库最上面一行。比如「有序数组 + 查找 → 二分;连续子数组 + 区间最值 → 滑动窗口或前缀和;树形结构的路径/深度 → 先想递归返回什么」。写不出来这个条件,说明这道题还没真正内化。
4.4 刷到100题,前面的专题开始遗忘
现象:刷到第120题时回头做第20题的同类题,感觉很陌生,解题时间比第一次还长,心态直接崩。
原因:没有间隔重复。大脑对「刚学会但不常用」的信息遗忘速度极快,当时做对了只代表短期记忆,不代表长期掌握。
解决:给错题本设两个复核时间点:一周后、一个月后。每周六不学新题,只重做本周错题;再到整月节点,把错题本里标记「一个月」的题全部过一遍。遗忘的本质不是记性差,是复习节奏没跟上。这个坑最隐蔽也最致命,因为当事人常常以为是学得不够,结果是复习不够。
4.5 数据结构选型翻车:栈、队列、set、dict没建立条件反射
现象:遇到「设计一个支持常数时间访问的队列」「判断括号匹配」这类题,要想很久才选对数据结构,甚至选错导致复杂度不合格。
原因:对四种基础结构的适用场景没有形成条件反射,每次都是现场试错,面试现场根本来不及。
解决:整理一张选型速查表背下来:存在性与去重→set;键值映射与计数→dict;最近相关性(配对、撤销)→栈;排队与层级处理→队列。面试里先说「我用XX结构,因为XX」,再说代码,这句话本身就是得分点。选型说错了,代码写对了也救不回来。
注意:踩坑不可怕,可怕的是踩完不知道自己错在哪里。上面五条,每一条都可以靠改流程而不是加练题量解决。
5. 从「看得懂题解」到「面试讲得清」:复述训练与变式迁移
刷题最终要过面试这一关。面试考的不只是写对,还有「在规定时间内讲清楚思路」。这需要单独练,不能靠刷题顺带完成。
5.1 四步话术:把一道题讲成一个两分钟有逻辑的故事
我给自己定了一个固定的讲解框架,每道题的讲解都按四步走:
第一步,题目转译。不念原题,用自己的话说清输入、输出、约束。比如两数之和,我会说:「给一个整数数组和一个目标值,返回两个下标,它们对应的值加起来等于目标,每个输入恰好一个答案。」这一步的目的是证明你读懂了题。
第二步,思路推导。先给暴力解,再说怎么优化。暴力是两层循环O(n^2),优化的点是把内层的「查找」换到哈希表里做O(1)命中,整体O(n)。
第三步,代码走读。不逐行背,只讲三行关键代码:为什么建立哈希表、为什么先查再存、为什么返回之前查到的下标。
第四步,边界与复杂度。主动说:「nums只有一个元素时查不到,返回空;最坏情况所有元素都互不相同,空间复杂度O(n)。」
一个合格的讲法大约两分钟。讲的时候不要背,而是像在向同事解释你的代码一样自然。我自己练的时候会对着镜子讲,后来发现录音回放效率更高。
5.2 自己当面试官:录音回放与自问清单
没有真实面试官的时候,自己组局。我会把题目往手机上一放,用自带的语音备忘录录音,当作模拟面试。讲完回放,只听两件事:
一是卡壳的地方。超过3秒没说出话的位置,大多是思路没理顺,在错题本上标「表达生疏」。二是逻辑连接词。如果通篇都是「然后、然后」,说明只是在回忆步骤,没有理解步骤之间的关系;如果能在每步之间用「因为…所以…」串起来,才算真的懂。
自问清单也固定下来:输入为空怎么办?元素全部相同怎么办?整数溢出吗?如果数据量翻倍,现在的复杂度还行吗?这四个问题覆盖了大多数边界翻车。
5.3 二刷过滤器:用三条标准判断一道题是否真的掌握
刷完不等于掌握。我判断一道题能不能过,用三条标准:
| 标准 | 具体表现 | 达标线 |
|---|---|---|
| 隔周重做 | 不看解析,独立写出并通过 | 正确率80%以上 |
| 讲得出「为什么」 | 能说清这个解法为什么是最优,而不是「我做过这题」 | 连续3次都讲顺 |
| 能做变式迁移 | 两数之和改动成三数之和、最接近目标值,能说出改动点 | 现场能推导 |
如果一道题里有两条满足不了,把这道题退回第二层「独立重写」,而不是直接翻篇。变式迁移是最硬的一条标准:它证明你掌握的是问题结构,不是题目原文。算法面试考的是迁移能力,不是记忆力。
6. 200题刷完后的三个习惯:错题本、模板库与每周自测
刷完200题不是终点,收尾阶段这三个习惯能让前面所有功夫不白费。
6.1 错题本:只记卡点,不抄题解
我的错题本每行一张表的格式:
| 日期 | 题名 | 题型 | 卡点 | 一句话解法 | 下次复核 |
|---|---|---|---|---|---|
| 第X天 | 两数之和变体 | 哈希 | 没想到先查再存 | 查找用哈希,边遍历边查 | 1周后 |
这里的关键是「一句话解法」必须用自己的话说,写不出来就说明这道题还没消化。整本错题本不该出现超过一行的完整代码,抄代码是抄给别人看的,记卡点是记给自己用的。把错题本记成题解合集,是这阶段最常见的翻车方式。
6.2 模板库:按「识别条件、复杂度、框架代码、易错点」组织
模板库我用一个Markdown文件维护,每个模板五段:识别条件、复杂度、框架代码、易错点、典型题。文件控制在15个模板以内,超过就合并。模板库不是网上题解的大杂烩,只留你自己写过的、经过独立重写的版本。
面试前一周,我只做三件事:把模板库一页纸打印出来过一遍、把错题本扫一遍、每天限时做一道中等题热身。不开新题,不碰难题,保持手感比挑战边界重要得多。这个流程看起来很朴素,但每次都能让状态快速回到正轨。
6.3 每周自测:周六限时混题,周日重做错题
平时周一到周五是按专题刷的,但面试不会提前告诉你今天考哪个专题。所以每周一定要有「混题」时间:周六限时45分钟做3道不同专题的题,周日重做本周错题。混题自测的目的有两个:一是训练「看到题目先判题型」的反应速度,二是检验这一周的知识是不是真的稳了。如果混题正确率低于70%,下周不要开新专题,把本周的内容再巩固一轮。
我最后想留一个具体的教训。有次面试,面试官把有序数组查找改成「先旋转再查找」,我上来就默写二分模板,边界条件全部想错,在代码写到一半的时候就已经知道不对劲了。那次之后我给自己立了个规矩:任何模板,先说出「这个模板的边界在哪里」再动手写。这个习惯让我在后来的面试里稳定了很多,表面上是多了一句废话,实际上是在强迫自己从「背代码」切换到「讲思路」。同样在刷200题的你,如果现在还在为「刷完就忘」发愁,大概率不是刷得不够,是复习得太少。希望帮到你。
本文还有配套的精品资源,点击获取