简介:一份面向程序员求职与算法进阶的刷题全攻略资料包,整合剑指Offer题解、程序员代码面试指南题解、九章算法讲解、牛客直通BAT算法课等经典内容,同时收纳大公司笔试真题与LintCode编程题练习,基本覆盖算法面试常见题型与解题思路。代码按学习阶段组织,既保留第一遍学习时的原始实现,也附带了两个月后复习时全部重新编写的版本,通过两版对比可清晰看到思路演进和代码优化过程;其中涉及动态规划、二叉树遍历、最大子矩形、分隔回文串等典型问题,适合反复研读。资源共969个文件,以468个Java源文件和493个编译生成的class文件为主,辅以md、txt、docx说明文档及gitignore工程配置,压缩包整体仅789KB,轻量便携而目录结构清晰。目前已有41人学习下载,可用于系统备战算法面试、快速回顾核心题型,也适合温习后自测代码能力。
1. 数据结构与算法刷题全攻略项目:解压后别急着吃灰,先把它拆成两遍学习计划
“数据结构与算法刷题全攻略项目”这个名字,在牛客、B站和GitHub上被反复搜,因为它把校招和社招面试最常用的几套东西打成了一个包:剑指Offer题解、程序员代码面试指南题解、九章算法讲解、牛客直通BAT算法课,外加lintcode和大公司笔试真题。这些资源单看都不稀罕,真正难的是怎么用起来。我见过太多人解压后拖进网盘吃灰,也见过有人只抱着“第一遍学习代码”反复看,完全不知道还有个“两个月后复习全部重新实现代码”的目录。这篇笔记就讲我实际跑通的用法:第一遍怎么学,复习重写怎么练,笔试真题怎么排雷,最后把刷题记录变成自己的面试弹药。
2. 先搞清压缩包里有什么:四套题源分工与两版本代码的目录组织
2.1 从剑指Offer到牛客直通BAT:四套题源怎么分工
这类压缩包最忌讳一把抓。剑指Offer、程序员代码面试指南、九章算法、牛客直通BAT这四套资料,覆盖的是不同层次的需求,用错了顺序等于白刷。
我一般这样拆:
- 剑指Offer题解:适合第一遍打底。题目都是面试高频经典,每道题都能讲清楚思路和边界,拿来建立“见到题会套方法”的肌肉记忆最好。它的题解代码往往很短,但隐藏了很多坑,比如链表为空、数组越界、输入非法。
- 程序员代码面试指南题解:更偏算法设计,题目比剑指Offer难一档,很多题会考你“能不能把O(n^2)优化到O(n log n)”,适合在剑指Offer过完一遍后再提升。
- 九章算法讲解:按专题组织,二分、双指针、动规、图论分开讲。这不是拿来刷的,是拿来查的。哪个专题薄弱就去补哪个,比自己硬啃效率高得多。
- 牛客直通BAT算法课和笔试真题:其实是同一件事的两面。课程讲的是大厂笔试常用套路,真题是真刀真枪的输入输出格式、数据规模和时限要求。这部分放到复习重写阶段做模拟,最能暴露问题。
这个项目名的卖点在于“第一遍学习代码”和“两个月后复习全部重新实现代码”是两套独立的文件。很多人没意识到这两套文件才是整个项目的核心。题解和课程只是输入,你自己的两遍实现才是输出。所以解压后第一步不是打开题解,而是看清楚目录结构,别把两遍代码混在一处。
2.2 第一遍学习代码与两个月后复习全部重新实现:为什么留两版本
我第一遍学的时候,代码是带着注释的、照着题解思路写的,可能还夹杂调试语句。两个月后,这些代码基本忘了一半。这时候如果翻出旧代码看,只会产生“我好像会了”的错觉。真正有效的做法是:不打开旧代码,只留题目列表,从头重新实现一遍。两个版本放在一起对比,才能看到自己哪里还记得、哪里真的忘了。
这里有一个认知误区:看你觉得自己记住了没用,写出来才算。第一遍代码其实是“学习笔记”,不是“答案库”。它记录的是我当时的思路、卡点、复杂度推演。两个月后重写代码,则是把当时的思路在同一边界条件下再推演一遍,能写通才算过。两版代码的差异,就是学习的证据。
所以目录里必须明确区分这两层。我会把第一遍代码放在first_round,重写代码放review_rewrite,两个目录之间不做任何软链接,也不允许互相引用。哪怕同一道题完全不用改,也要重新复制一份并写明“复习重写,与第一次一致”,这本身就是一次确认。
2.3 代码目录怎么组织:按公司、按标签、按时间三种方案
整理目录时常见做法有三种:按题源分、按算法标签分、按时间分。我最推荐的是“先按轮次,再按题源,最后按标签”三层。
algorithm-prep/ ├── 01_first_round/ # 第一遍:跟题解学思路 │ ├── jianzhi_offer/ │ │ ├── 03_数组中重复的数字.py │ │ ├── 04_二维数组中的查找.py │ │ └── 11_旋转数组的最小数字.py │ ├── jiu_zhang/ │ │ ├── two_pointers/ │ │ │ ├── 01_最长无重复子串.py │ │ │ └── 02_合并两个有序数组.py │ │ └── dp/ │ └── lintcode/ │ ├── 1xxx_xxx.py │ └── 2xxx_xxx.py ├── 02_review_rewrite/ # 两个月后:关掉旧代码重写 │ ├── jianzhi_offer/ │ ├── programmer_code_guide/ │ └── company_exam_2025/ ├── 03_company_exam/ # 大公司笔试真题 │ ├── 某大厂笔试_2024_秋招/ │ └── 某大厂笔试_2025_春招/ ├── 04_notes/ # 错题笔记,按标签记 │ ├── 排序算法/ │ └── 贪心/ └── README.md这个结构的好处是:第一轮和第二轮不会互相污染。如果你按题源或按算法标签分,第一遍和第二遍代码混在一起,很难强迫自己不看旧代码。我在 README 里还会写清楚每个文件名的规则:题目编号_题目名_方法名.py。方法名很重要,比如双指针、快排、剪枝,一眼就能知道这题考什么。
命名定了之后,搜索也省力。面试前想突击二分,直接find review_rewrite -name "*二分*",把所有重写过的二分题拉出来再过一遍。这里我特别建议把README.md当作“刷题地图”,上面写目前哪些专题过了一遍、哪些重写还没完成,时刻知道自己的进度。
3. 第一遍学习代码怎么用:从看懂题解到写出来的闭环
3.1 第一遍的关键是“想清楚”,不是“写出来”
第一遍接触新题,最忌直接抄题解。我的流程是:先看题目,给五分钟自己推。五分钟推不出来,再看题解的第一段思路描述,不看代码。然后再自己白板写。这样写出来的代码就算和题解长得完全一样,也经过了一次自己的思考。
很多题看似是“不会”,其实是“没见过套路”。比如剑指Offer里的旋转数组最小值,第一次见可能懵,但知道二分套上去之后,后面再做类似题目就有方向了。第一遍就是建立套路库。做题时我会顺手把这道题的暴力解法和优化解法都写一遍。比如一道“两数之和”,先写暴力枚举算法,再把哈希或双指针写出来。这个过程能直观看到算法优化的收益,而不是背结论。
这也是为什么题解里明明给了最优解,我还会先写暴力。暴力代码短,逻辑直,不容易错。写完暴力再跑样例,确认理解题意了,再去想怎么剪枝、怎么降复杂度。从暴力到剪枝的路径,才是第一遍真正该学的。很多资料只给最终答案,漏掉了“暴力为什么慢、剪枝怎么剪”的思考过程,这套项目的九章算法和代码面试指南恰恰补上了这一步。
3.2 用注释记录思路而不是粘贴答案
第一遍学习的代码里,注释比代码更重要。常见错误是题解里带什么注释就抄什么,过一个月再看毫无记忆点。我自己的做法是:按自己的话写注释,只写三件事——思路切入点、关键边界、复杂度。
# 二分查找模板:查找有序数组中第一个 >= target 的位置 # 思路:维护 [left, right] 区间,mid 满足条件就收缩右侧 # 注意:终止条件是 left < right 而不是 left <= right # 边界:数组为空直接返回 0;target 比所有值大都返回 len(nums) def lower_bound(nums, target): left, right = 0, len(nums) # 右开区间,方便处理未找到的情况 while left < right: mid = left + (right - left) // 2 # 防止 left+right 溢出 if nums[mid] < target: left = mid + 1 else: right = mid return left这段代码的注释不是题解原话,而是我自己的理解。“右开区间”和“终止条件”是我当初写错两次的地方,必须重点标出来。left + (right - left) // 2是处理整型溢出的通用写法,在 C++ 里尤其重要,Python 里也养成习惯。
第一遍学习的每个文件都应该包含这样的注释。等到两个月后重写,我要求自己先写注释、再写代码,因为注释能检验我是不是真的理解了:如果注释写不出来,说明思路根本没建立。这比白板题还严格。
3.3 一道题跑通后,必须做的三件事
很多人刷题是“提交通过=结束”。我后来改成“通过+三步”,刷题量虽然少了,但记住的时间长了很多。
第一步,写测试用例。剑指Offer的题目都有例子,但远远不够。我会补空输入、单元素输入、全重复输入、最大输入,把边界都跑一遍。比如链表题一定会测试头结点被删除的情况。第二步,算复杂度。不只看题解写的结论,而是自己把循环数一遍,写清空间用了几个临时变量。第三步,和题解对比。我的实现可能通过,但可能代码冗余、可读性差。打开题解看它怎么精简,把差异点记下来。
这三件事都不难,但很容易偷懒。我做过统计:直接刷题不记笔记,两周后能复现的题不到三成;做了这三步的题,两个月后重写时大部分能独立做出来。这也是为什么这个攻略项目把“复习重写代码”单独列出来——它就是给这三步做验收的。
4. 两个月后复习重写:重新实现代码的正确姿势
4.1 复习重写不是背答案:先看题、再写、最后对比
两个月后重新实现,最容易踩的坑是“凭记忆抄旧代码”。大脑会对见过的东西产生熟悉感,看到题目就想翻旧代码。我应对的办法是物理隔离:重写时把旧代码目录整个折叠,编辑器里只打开题目列表,绝对不看旧文件。如果一道题真的卡死超过三十分钟,宁可标记“重写失败”,也不要看旧代码。看一次旧代码,这次重写就废了。
重写时我会给自己限时。单题不超40分钟,其中前20分钟用来想思路,后20分钟写代码和自测。这样做是为了模拟笔试的紧张感。笔试真题里一道编程题平均也就四十分钟,所以平时就该用这个节奏练。
4.2 重写代码时怎么控制时间与边界
重写阶段最重要的是“卡时间”。我手机开倒计时,四十分钟到,不管写没写完都要停。这样做的原因是:真实笔试时你不可能无限时,而且笔试常见问题不是不会做,是会在某道题上死磕,导致后面简单题没时间写。把限时训练练成习惯,笔试心态会稳很多。
边界条件第二次写时依然容易漏,我列了一个自查清单,每个文件开头写好待确认边界:
# 边界自查: # 1. 输入为空 / 长度为0 # 2. 长度=1 # 3. 全部元素相等 # 4. 最大值/最小值(int溢出或超大数组) # 5. 目标不存在/超过范围这个清单不是每次都要逐条写,但它能提醒我在重写时不要只顾主路径。比如排序算法里,归并排序很容易在递归合并时把索引写错;二分查找很容易在left = mid + 1还是left = mid上翻车。重写时我会刻意在核心循环处加断言,比如:
assert 0 <= mid < len(nums)assert不能用于线上提交,但在本地自测时非常管用,能立刻暴露索引越界或死循环的问题。笔试提交前我会删掉所有断言。
4.3 用diff和测试用例校验两版代码的差异
重写完成后,对比两版代码是最有价值的一步。我不用肉眼一行行对比,而是用命令行工具:
diff -u 01_first_round/jianzhi_offer/11_旋转数组的最小数字.py 02_review_rewrite/jianzhi_offer/11_旋转数组的最小数字.pydiff会输出两个文件的差异。如果是空输出,说明两次实现完全一致,这通常意味着我第一次学的思路还完整在脑子里。如果差异很大,我需要判断:是重写实现更简洁了,还是把原来的正确版本改错了。我还会把两个版本都跑一遍同一组测试用例,用结果说话。
python3 01_first_round/jianzhi_offer/11_旋转数组的最小数字.py python3 02_review_rewrite/jianzhi_offer/11_旋转数组的最小数字.py如果两个输出相同,说明行为等价。如果不同,就把重写版的输出贴到自己的笔记里,标注“和第一版不一致,原因待查”。这一步常常能发现第一个版本里隐藏的 bug,也算意外收获。
5. 大公司笔试真题与lintcode刷题:避坑与常见排查
5.1 笔试真题和面试题的区别:数据规模、输入输出、时空限制
很多人在剑指Offer和lintcode上刷得飞起,一到公司笔试题却挂,原因就是没分清笔试和面试的区别。面试题通常让你写核心函数,输入已经帮你处理好了;笔试真题要求你从标准输入读取,按格式输出,处理多组用例,并且在严格的时间限制下运行。
比如 lintcode 上的题大多已经帮你封装好函数签名,你只要实现def solution(nums, target)就行。但大公司笔试真题往往是“读一个数组,再读一个整数,输出两行结果”,输入里的空格、换行、EOF都可能坑你。我在笔试真题目录里专门放了一个input_template.py,用来应对常见输入格式:
import sys def main(): data = sys.stdin.read().strip().split() if not data: return idx = 0 n = int(data[idx]); idx += 1 nums = list(map(int, data[idx:idx+n])); idx += n target = int(data[idx]) print(solve(nums, target)) if __name__ == "__main__": main()sys.stdin.read()一次性读取全部输入,适用于大多数在线评测系统。.strip().split()能处理行尾空格和空行,比input()循环不容易出错。笔试时最怕输入解析出问题,这个模板我每次笔试前都会复制一份。
5.2 避坑:三个让刷题翻车的常见问题
我在这套攻略上摔过三个大跟头,每条都值得单独说。
第一个:看题解时觉得全懂了,关掉题解写不出来。现象是“看懂”和“写出”的鸿沟。原因在于第一遍学习时主动思考太少,大部分时间花在被题解牵着走。解决方法是刷题时强制自己先写注释再写代码,注释写不出就不允许开 IDE。这个方法笨,但有效。
第二个:复习重写时忍不住翻旧代码。现象是我一开始把两个版本放在同一个目录,写不出时就 fzf 搜题目,结果瞄到旧代码,思路一下被带偏。原因是对“复习重写”的规则不够硬。解决方法是把first_round目录改成只读权限,或放到另外的 git 分支。没有强制的物理隔离,重写大概率变成抄写。
第三个:刷题量上去了,笔试还是挂。现象是平时刷题每题都能过,但笔试时经常编译不过、超时或者卡输入。原因是平时只用了 IDE,没在真正的在线评测环境里跑过,也没严格卡时间。解决方法是每周至少做两场完整的牛客或 lintcode 模拟,把输入输出模板、时间盒都当成正式笔试来执行。
5.3 从题解到代码:怎么识别“假题解”与“真可跑”
这套项目压缩包里既然有题解,就免不了混入一些假题解。什么叫假题解?只有思路、没有完整代码;代码只有函数体、没有输入输出处理;代码有严重 bug,跑不了样例。
我识别假题解有三个标准:第一,看复杂度分析,如果题解没写时间复杂度或空间复杂度,多半是从哪抄来的一句话代码,不值得信。第二,看边界处理,一个可靠的题解必然会讨论空输入、单元素、重复元素。第三,实际跑一遍。我不会在本地随便跑,而是把代码粘到 lintcode 对应题目的编辑器里提交一次。能过才说这道题“会了”,不过就按失败处理,继续重写。这个习惯让我排掉了很多看着很有道理、实际跑不通的样例代码。
6. 把刷题记录变成自己的面试弹药库:三个进阶技巧
这套攻略用完整一遍后,最大的资产不是题解,而是自己写的两版代码和错题笔记。面试前我复习的顺序是:先翻04_notes里的错题,再挑02_review_rewrite里的题动手重写,最后用03_company_exam里的真题做模拟。但光这样还不够,我后来加了三个进阶动作,让刷题记录真正变成面试时的“弹药库”。
第一个动作是为每道重点题写“面试讲解稿”。面试官让你讲题时,不是只讲代码,而是讲思路。我在每个重写文件末尾加了一段注释,模拟向别人解释的样子:先说什么场景,再说什么解法,最后说为什么不是暴力。这段文字不提交给在线评测,但面试前过一遍会非常稳。第二个动作是给同一道题维护“多解法表”。剑指Offer里很多题都有暴力、优化、特殊技巧三种解法,面试时先说暴力再优化,展示思维深度。第三个动作是定时清理笔记,把已经稳定掌握的题移出重点区,只保留反复出错的。我面试前一周就只盯这些错题。
有一次我面试被问到一个堆排序算法的变体,正好是我两个月前重写过的原题,但第一次实现时堆的调整方向写反了。好在我重写时单独标记了“易错点”并写了讲解稿,现场才没翻车。这让我更确信:刷题攻略的价值不在于收集,而在于你拿它做了什么。希望这套“两遍写法”对你有用。
本文还有配套的精品资源,点击获取