
简介一份面向程序员面试与算法进阶的刷题全攻略项目覆盖剑指Offer题解、程序员代码面试指南题解、九章算法讲解、牛客直通BAT算法课等主流内容既含第一遍学习代码也有两个月后复习时全部重新实现的版本适合备战大厂笔试、系统训练编程题的求职者。压缩包共969个文件以468个Java源码和493个class编译文件为主辅以2个list简介、2个iterator、md/txt/docx笔记及gitignore配置包体仅789KB目录按课程模块划分便于对照题解检索。已有41人学习下载。内容涵盖回文分割、最大子矩形、按层打印二叉树等典型算法题通过初版与复习版的代码对比可清晰看到解题思路的优化过程同时附带大公司笔试真题编程题既适合日常刷题积累也适合笔试前集中复盘与查漏补缺。整体结构清晰适合不同阶段反复对照练习。1. 一套刷题资源能解决什么从「看题解说我会了」到「合上书能重写」这个标题把算法面试准备途中会用到的东西几乎收全了剑指Offer题解、程序员代码面试指南题解、九章算法讲解、牛客直通BAT算法课、第一遍学习代码、两个月后复习重新实现的代码再加上大公司笔试真题和 lintcode。真正有价值的不是这些资源本身而是它背后那条“先学框架、再动手写、隔一段时间重写一遍”的闭环。很多人刷题失败不是题做得不够而是刷过的题第二周就不认识了面试时讲思路头头是道一上手写就卡在边界条件和输入输出上。这个项目适合三类人准备校招或者跳槽、想把算法面试系统过一遍的开发者自学数据结构与算法但缺一份带路径的题库的人以及刷了三四个月还在原地打转、需要一份复盘清单定位问题的人。接下来的内容我按这个标题拆开讲清楚每部分怎么用、代码怎么组织、复习的节奏怎么排。2. 拆开标题里的四类资源剑指Offer、代码面试指南、九章算法课、牛客真题的配比用法只把资源下载下来存到硬盘里这个项目的价值基本为零。刷题攻略的核心是“配比”什么阶段用哪套资源、每套资源解决什么问题、哪些可以并行哪些必须先后。我按自己的经验把标题里的四类内容拆成四个模块来讲。2.1 剑指Offer题解建立面试题标准解的坐标系剑指Offer的题在面试里被翻牌的频率依然很高尤其是链表、二叉树、栈与队列这几章。它的题解价值不在“给一个能过的答案”而在于每道题都对应一类通用解法看到“从尾到头打印链表”你该想到栈或者递归看到“重建二叉树”你该联想到前序中序的切片规律看到“连续子数组最大和”直接落到动态规划的状态定义上。用这套题解的正确姿势不是从头到尾看题解再做题而是先自己写写不出来才看题解看完把题解折起来自己默写一遍。每道题留一个笔记条目记录你当时的思路、标准解的差异点、以及最容易错的边界。比如链表题里的空指针、二叉树题里的递归出口、数组题里的越界都是高频翻车点。我一般会按专题刷而不是按题号刷。把链表、树、动态规划、字符串分开每块集中两周左右过完。题解笔记的格式不复杂记三行就够## 剑指 Offer 22链表中倒数第 k 个节点 - 我的解法快慢指针快指针先走 k 步然后同步移动 - 易错点k 大于链表长度时直接返回 None头结点为空 - 复杂度时间 O(n)空间 O(1)这段笔记的核心作用是给两个月后的自己看的。到复习阶段直接翻这份 markdown就能快速回忆起每道题的考点和当年踩过的坑不用重新读一遍题解。2.2 程序员代码面试指南LeetCode 之外的现场编程训练这本书的定位不是入门是拔高。它的题解特点是“把为什么要这样设计状态讲得很细”尤其适合在完成第一轮剑指Offer之后用来做刻意练习。这本题库里的题目风格更接近真实面试中的白板题题干长、边界隐蔽很多题一眼看不出考察点比常规刷题平台上的题更考验拆解能力。常见做法是配合牛客网上的对应题库同步刷。拿到一道题先自己独立思考二三十分钟再看题解。这本书的题解普遍偏长适合拆着读先读“思路”部分自己把代码写出来再对照“最优解代码”逐行比较。遇到链表、二叉树这类结构复杂的题我建议把题解的递归过程手动画一遍调用栈比盯着代码看有效得多。归并排序、堆排序这类排序题目书里也给了不少变体比如链表排序、几乎有序数组排序这些都是笔试里的常客值得反复做。2.3 九章算法讲解与牛客直通BAT算法课体系化框架的两种打开方式九章算法的核心价值是帮你把题型体系建立起来二分法、双指针、宽度优先搜索、深度优先搜索、动态规划、贪心每个模板都有一套固定的代码骨架。这不是让你背代码而是让你在拿到新题的时候能快速判断“这题属于哪个类别可以用哪套骨架去套”。牛客直通BAT算法课的优势则在于贴近国内大厂的出题风格讲题时会顺带提面试官的考察点适合在建立体系之后用来查漏补缺。这两门课的正确用法是“边看边写”不是“看完再写”。我习惯每看完一个章节立刻把课上讲过的例题自己重写一遍不许看老师的代码。比如动态规划章节讲完背包问题我就在本地把 0-1 背包、完全背包、多重背包各写一遍跑几个用例再继续往下看。写题解模板时常见的 DFS 骨架是这样的def dfs(pos, visited, path, result): # pos: 当前搜索位置 # visited: 记录节点是否访问过避免死循环 # path: 当前搜索路径回溯时需要撤销 if is_target(pos): result.append(path[:]) return for nxt in neighbors(pos): if nxt in visited: continue visited.add(nxt) path.append(nxt) dfs(nxt, visited, path, result) path.pop() visited.remove(nxt)这里的参数设计有几个细节path 用 path[:] 拷贝后存入 result否则后续回溯会污染已保存的结果visited 在递归返回后要删除当前节点这是剪枝之外的另一个关键点。如果漏了 visited.remove(nxt)同一层循环里其他分支就会错误地跳过未访问节点。很多同学写 DFS 翻车都出在这两行上。2.4 笔试真题与 lintcode从刷题到考试的最后一公里大公司笔试真题跟平时刷题最大的区别在于输入输出。平时在刷题平台提交只需要写核心函数笔试则要处理多组输入、EOF 结束、字符串解析、时间空间限制。标题里的大公司笔试真题作用就是让你提前适应这种“考试节奏”。lintcode 在这里的价值是平时的限时训练。它按公司、按专题做了题目标签还有不少题目和国内大厂风格接近。第二阶段复习时我会每周抽一个完整下午按目标公司的 tag 随机选题限时 60 到 90 分钟做一套模拟笔试场景。检验标准很简单时间到就停不管做没做完然后逐题复盘。这样做的好处是真正笔试时你对时间的感知是准的知道一道 medium 应该在 20 分钟内拿下超过 25 分钟就该换题。资源定位使用阶段单日用量剑指Offer建立面试题坐标系第一遍学习4 题/天代码面试指南拔高与变体训练第一遍后期 复习期2 题/天九章算法课题型模板体系最前期1 节/天牛客直通BAT课大厂风格补充复习期按需选看笔试真题模拟考试最后一个月1 套/周lintcode日常练习 限时模拟全程2 题/天这套配比不是死的但原则是固定的前期把剑指Offer和算法课铺开中期用代码面试指南拔高后期用真题和 lintcode 做模拟。千万别在前期就去做大厂真题你会被打击到怀疑自己。3. 第一遍学习代码的工程化写法目录结构、注释规范与暴力优先第一遍学习阶段最大的坑是代码写完就扔没有任何组织。等到两个月后复习时你面对的是几百个文件名是test.py、1.py、new.py的文件根本分不清哪题是哪题。所以第一遍就要把代码当工程来管理。3.1 按专题建目录不按平台建目录很多人的刷题目录是leetcode/1_two_sum.py、lintcode/xxx.py按平台分按题号排。这种组织方式最大的问题是复习时你没法按知识块批量过题。链表题散落在各个平台目录里动态规划题也见不到彼此。我习惯的目录结构是纯专题化mkdir -p algorithms/{array,linked_list,tree,dp,string,backtrack,sort} cd algorithms tree -L 2这样一个专题一个目录刷剑指Offer的时候按表驱动往里放题刷 lintcode 的时候也按表驱动放进对应专题。目录名用 snake_case避免出现空格和中文防止某些工具链出幺蛾子。专题粒度不要太细比如不要拆出binary_search_tree和heap这种控制在 array、linked_list、tree、dp、string、backtrack、sort、math 这八个左右就够用太细反而会纠结一道题该放哪。树和链表这种靠结构说话的题还可以在 tree 目录下加tree/notes.md把遍历方式、递归模板、Morris 遍历这类知识点单独记录。这样做的好处是两个月后重新实现代码时你翻开这个文件就能回忆起整个专题的骨架。3.2 每题一个文件夹题解、代码、测试三件套第一遍刷题时我不建议直接把代码写成单文件扔进目录。养成一个小习惯每道题建一个文件夹放三个文件。这个习惯前期看起来费事后期收益极大尤其是复习重写的时候能直接跑回归。algorithms/tree/26_is_subtree/ ├── problem.md ├── solution.py └── test_solution.pyproblem.md记录题干、你的解法思路、题解里更优的思路格式就是第 2 章讲的三行笔记solution.py是你的通过代码test_solution.py是测试用例。以二叉树子树判断这道题为例solution.py长这样# solution.py class Solution: def has_subtree(self, root_a, root_b): 判断 root_b 是否为 root_a 的子树。 思路当前节点相等则完整比较否则递归左右子树。 复杂度时间 O(m * n)最坏情况下每个 A 节点都要比较一棵 B 空间 O(h)h 为递归深度。 if not root_b: return True if not root_a: return False if self._same_tree(root_a, root_b): return True return self.has_subtree(root_a.left, root_b) or self.has_subtree(root_a.right, root_b) def _same_tree(self, a, b): if not a and not b: return True if not a or not b: return False return a.val b.val and self._same_tree(a.left, b.left) and self._same_tree(a.right, b.right)注释里写清楚三件事输入参数的含义、核心思路、时间空间复杂度。代码本身的写法要注意递归出口的顺序先处理root_b为空的情况再处理root_a为空顺序反了在特殊用例下会返回错误结果。这个细节在剑指Offer原题上是有争议的面试时最好跟面试官确认“空树算不算子树”的题面定义笔试就按题目里给的说明来。test_solution.py里放的是基础用例、边界用例和随机用例三部分。基础用例验证正常逻辑边界用例覆盖空树、单节点、重复值随机用例用于回归。测试文件的写法不用复杂用 Python 自带的assert就够了。3.3 第一遍的验收标准跑通用例、标注思路不追求最优解第一遍学习的核心目标是“铺开题量”不是每道题都写到最优。很多人第一遍就死磕最优解一道题看三四个题解方案最后整个进度烂尾。我的做法是每道题先用自己能想到的办法解出来哪怕是暴力枚举能跑通就是完成然后对照题解把更优的思路记到problem.md里代码不一定要重写成最优解。比如 KMP 算法这类字符串匹配的经典题第一遍我可能只写出暴力匹配的版本然后在笔记里标注“可用 KMP 优化为 O(nm)”。对第一遍来说暴力版代码已经能帮你建立这道题的思路坐标KMP 的优化留给复习阶段去啃。做题结尾过一遍 checklist独立写代码、用例跑通、复杂度标注、对照题解补优化思路。这套流程跑下来第一遍的速度会明显比“每题追求最优解”快而且覆盖的题量更大这对后续二刷至关重要。4. 两个月后全部重新实现二刷的验收标准与复习节奏标题里特意写了“两个月后复习全部重新实现代码”这个设计值得单独说透。二刷不是把旧代码看一遍也不是重抄一遍而是“不看旧代码把所有题重新写一遍”。这才是检验真正掌握程度的方式。4.1 复习周期怎么排把题目分成三档滚动复习两个月不是从刷完第一天开始算而是刷的过程中就要滚动复习。我的节奏是按记忆曲线粗略排的分三档管理需要复习、生疏、错误。时间点复习对象验收标准第 1 天新做题独立跑通用例标注复杂度第 3 天标「需复习」的题合上题解手写一次第 7 天标「生疏」的题一次通过全部测试用例第 14 天标「错误」的题能说清上次错在哪第 30 / 60 天整个专题不看旧代码整体重写标记不是靠感觉而是做题时在problem.md里加一行status: review。哪道题是看题解才写出来的哪道题是一遍过的哪道题的测试用例挂了两次这些状态都要记下来。二刷时直接筛选status: review和status: error的题优先处理其他题可以放到后续统一重写。这个过程有个额外作用它会暴露你第一遍“假会”的题。第一遍靠看题解通过的题到第 7 天重写时大概率写不出来这不是坏事这说明你的知识盲区在这里。4.2 重写验收标准不开旧代码、不查笔记、一次通过二刷阶段我会建一个独立的v2目录和第一遍的v1目录并行存在让旧代码留在原地但重写时绝对不能打开它。mkdir -p v2/tree cp v1/tree/26_is_subtree/test_solution.py v2/tree/26_is_subtree/ python -m pytest v2/tree/26_is_subtree/test_solution.py这里只复制测试文件不复制 solution。写完新代码后直接用pytest跑回归。全部用例通过之后再打开 v1 的旧代码做对比diff -u v1/tree/26_is_subtree/solution.py v2/tree/26_is_subtree/solution.pydiff -u输出 unified diff能直观看到两个版本的差异。这一步的意义不是比谁写得好而是帮你看到自己的思维变化第一遍绕了远路的地方第二遍是不是直接想到了更简单的写法第一遍忽略的边界第二遍是不是天然就处理了。这些变化才是二刷的真正收获。二刷的验收标准有三个层次依次递进不看旧代码能独立写完这是及格线全部测试用例一次通过这是良好线能解释清楚自己的代码比 v1 好在哪这是优秀线。达不到哪个层次就把那题再标回status: review等下一轮滚动复习。4.3 从“能跑”到“可读”二刷要改掉的三类代码坏味道二刷不只是为了过题还是顺手改掉第一遍代码里坏味道的好机会。最常见的问题有三个变量名无意义、递归或循环的边界条件散落、复杂度没有标注。第一遍写代码用a、b、tmp没问题因为目标只是跑通二刷时就要改成root_a、target_sum、node这类能直接表达含义的名字。第二个问题是边界条件处理比如链表翻转类题目第一遍经常写出while cur and cur.next这种靠试出来的判断条件二刷时应该能把“何时结束循环”的推导过程想清楚并在注释里写出来。第三个问题是复杂度这是面试必问项docstring 里没有复杂度标注的题二刷时补上。这些改进看起来琐碎但两个月后你再看v2目录每道题的代码都可以直接拿去做模拟面试的手写题不需要再花时间回忆当时的思路。5. 刷题避坑与常见问题五个让进度翻车的坑这套流程我在自己和带过的同学身上反复见过几类问题单拎出来写清楚。5.1 现象一刷就卡在“看题解上头”一天过去只过了三题原因很简单贪多求优。每道题都想把最优解吃透看题解花了四十分钟自己写又花了四十分钟一道题耗掉一个晚上。这种状态下刷到第十几天就会因为进度太慢而放弃。解决的办法就是 3.3 那条铁律你的第一遍只需要暴力解加笔记。暴力枚举能过的题直接提交跑通把优化思路写进problem.md立刻做下一题。一天四题比一题啃到底对建立整体知识框架有效得多。5.2 现象二刷时打开v1里的代码发现自己读不懂这是很多人的血泪教训。第一遍写完代码没有注释、没有复杂度、没有思路记录两个月后看那段代码跟看陌生人写的一样连变量名什么意思都要猜半天。唯一解法是第一遍写代码时就按第 3 章的规范留好三件套。没有配套测试的代码二刷时根本无法验证重写的对不对没有思路注释的代码重写时还是会走一遍第一遍的弯路。所谓“留后路”其实就是把第一遍的思考过程固化下来。5.3 现象只刷剑指Offer和 lintcode笔试环节直接崩剑指Offer和 lintcode 的提交方式都是只写核心函数不需要处理输入输出但大公司笔试是 ACM 风格要自己解析输入、处理多组数据、判断文件结束。平时不练这套笔试时连输入解析都要花二十多分钟。解决办法是把第 2.4 节的限时模拟提前到复习期每周至少一次完整套题模拟。同时平时刷题时多用牛客网的在线笔试环境它的输入输出风格和大厂笔试更接近跟纯函数提交的平台侧重点不同两个环境交替用才不容易偏科。5.4 现象代码能过用例但提交全靠撞边界这是“平时只跑 happy path”造成的。比如数组题不测空数组、全重复数组、长度为 1 的数组链表题不测头结点为空、只有头结点、循环链表。这些边界在笔试用例里是必测的第一遍标「错误」的题大多数都是边界挂的。解决方法是test_solution.py里强制写三类测试基础用例、边界用例、随机用例。写代码之前先在problem.md里列出你能想到的边界条件写完之后逐一验证。坚持一个专题你会发现自己对边界的敏感度明显提高。5.5 现象复习节奏被项目打断后直接断档整个计划作废刷题最怕的不是难而是中断。项目一忙停了两周再打开v1目录时完全不想动。我的解法是设计“低功耗重启模式”断档后不要从头开始从status: review里的题挑最容易的三道用半小时写掉找回手感。然后按 4.1 的表格把整个专题的复习计划顺延而不是重开一版计划。复习周期的容错设计很重要宁可每轮少覆盖一点也要保证滚动节奏不被一次中断打断。6. 从刷题量到考试分限时模拟与一题三问复盘二刷全部结束后最后一个阶段就是把“会刷题”转化成“会考试”。这一步很多人忽略总以为题刷够了自然考得好实际上笔试除了考算法还考时间分配、心态和临场判断。这套闭环走下来最容易拉开差距的就在最后一个月。6.1 用 lintcode 做限时模拟把每个周末当笔试过我推荐周末抽一个完整上午或下午在 lintcode 上按目标公司的 tag 随机选题组合一套 3 easy 2 medium限时 75 分钟模拟。设置一个计时器时间到了必须停笔然后对着一道题写“这题我卡在哪一步”的简短记录。模拟的关键是全程不翻开笔记、不查题解、不中途看手机跟真实笔试环境保持一致。如果连续三次限时模拟都能在规定时间内做完 2 到 3 题说明前期的刷题量真正转化成了考场上的反应速度。6.2 一题三问的复盘清单让每一套模拟都不白做每次模拟完我不会急着做新题而是把四道题逐一套用三个问题复盘这题在考哪个数据结构或算法类别我的解法比标准解差在哪一步如果数据量放大十倍我的解法还能不能过这三个问题问完你就知道下一周该往哪个方向补。比如三套模拟都卡在动态规划的状态定义上那就回翻九章算法的动态规划章节重点做线性 DP 的分类训练如果问题出在边界条件回看 4.1 里标「错误」的题重写一遍。这比盲目加题量高效得多。这套流程走完一遍的最大收益是你会获得一种对题目的直觉看到一道没做过的新题能快速定位它属于哪个专题、该用哪套模板、边界条件在哪里。这种手感不是靠三月份刷题量的堆积而是靠“第一遍铺量、二刷重写、限时模拟”这条闭环一步步磨出来的。我在带人时最常说的一句话是刷题不是写代码是做项目你得有自己的版本管理、测试用例和复盘记录。这个习惯坚持到现在每次面试前我只翻v2目录和notes.md就够了根本不需要重新刷一遍题。希望帮到你。本文还有配套的精品资源点击获取