多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

数据结构与算法刷题全攻略:重写一遍才是真正的学会

数据结构与算法刷题全攻略:重写一遍才是真正的学会 简介面向正在备战互联网公司技术面试的算法刷题者代码合集覆盖了剑指Offer题解、程序员代码面试指南题解、九章算法讲解以及牛客直通BAT算法课的练习内容高频知识点与常见题型均有涉猎。除第一遍学习时按模块整理的代码还收录两个月后复习时重新实现的全套版本前后代码可逐题对比帮助发现思路演进与易错点。同时包含大公司笔试真题编程题与lintc题库练习适合机试冲刺、查漏补缺与总结模板。压缩包共969个文件以468个Java源码和493个编译生成的class文件为主另有少量md、txt、docx说明文件与配置整体仅789KB轻量便携。目录按课程与专题划分便于快速定位目前已有41人学习下载对正处于系统刷题阶段或临近面试的开发者是低成本、高密度的复习参考。1. 这份刷题全攻略真正值钱的不是题解是「重写一遍」这个设计打开名为《数据结构与算法刷题全攻略》的压缩包时别急着把里头的题解当小说收藏。剑指 Offer 题解、程序员代码面试指南题解、九章算法讲解、牛客直通 BAT 算法课、大公司笔试真题这些名字凑在一起表面是一座资料山实际是一套设计好的学习闭环第一遍跟着学、照着写隔两个月把所有题重新实现一遍最后拿真题验证。很多人刷题是看题解看到会动手就卡壳本质是眼睛「懂」了思路手没有形成「会」的代码记忆。这份方案把学习周期拆成「学一遍 → 忘一遍 → 重写一遍 → 真题检验」四个阶段恰好能治这个病。适合三类人准备校招或社招算法面试的开发者、考研数据结构想拿代码题分数的学生、以及刷 LeetCode 刷到麻木想换个方法验证自己的人。它解决的只有一个问题让刷过的题半年后真的写得出来。2. 模块拆解五份内容不能平行刷得排出主次标题里列出的内容模块不少但绝大多数人拿到压缩包后的第一反应是打开剑指 Offer 题解从头开始一篇篇读读到第十题就放弃。这不是意志力问题是顺序错了。这套东西里的五块内容分别承担了「教材、补充、讲解、验证、真题」五个角色它们不是平行关系是一条从输入到输出的流水线。2.1 五块内容分别是干什么的一张表看穿编排逻辑先把标题里出现的每个模块和它在学习链路中的位置对齐知道每一份资料该在哪个阶段打开才不会浪费。模块核心作用在整条链路中的位置剑指 Offer 题解面试高频题的标准解法和思路第一轮主教材按题型顺序逐题过程序员代码面试指南题解偏工程化的写法、边界条件处理第一轮补充遇到不会的题再翻九章算法讲解算法专题系统讲解适合补知识盲区第一轮查漏补缺按专题回看牛客直通 BAT 算法课视频讲解动态画图辅助理解同步辅助看书看不进去时切换大公司笔试真题真实笔试的难度和出题习惯复习重写完成之后用于模拟这个模块设计的核心思路是「冗余覆盖」。剑指 Offer 题解负责高频题的标准答案程序员代码面试指南题解负责同一类题的工程化写法九章算法讲解负责把零散题目归纳成算法专题。同一个知识点在三个地方以不同粒度出现第一遍学的时候可能觉得重复但两个月后重写时你会发现记忆最牢的恰恰是那些被重复了三遍的内容。2.2 推荐的执行顺序三轮制而不是一轮制我建议把整个刷题周期拉成三个月分成三轮每轮一个月。第一轮第 1~4 周以剑指 Offer 题解为主线每天 2~3 题先看题、再写代码、最后对照题解同时打开牛客直通 BAT 算法课的视频只挑当天涉及的算法专题看比如今天刷链表就看链表那一节。第二轮第 5~8 周进入专题补漏按九章算法讲解的目录重刷一遍但换题做剑指 Offer 里没覆盖的题型在这轮补齐程序员代码面试指南题解作为字典用遇到不会的思路再查。第三轮第 9~12 周才碰大公司笔试真题每周两次完整模拟笔试。这个顺序有一个反直觉的点第一轮不要做题解之外的题。很多人一上来就开 LeetCode 随机刷刷到哪算哪最后两个月过去没有形成知识树。剑指 Offer 的题量有限、难度阶梯合理、题解质量稳定作为第一轮的「锚」最合适。别嫌它简单第一轮的目的是把常见数据结构和算法的代码模板刻进手部记忆不是挑战智商。2.3 为什么「复习重写」是一个独立模块而不是附加作业标题里单独列了「第一遍学习代码」和「两个月后复习全部重新实现代码」这是整套方案里最容易忽略却最关键的区分。第一遍学习的代码是「照着思路写出来的」两个月后重写的代码是「凭记忆从零写出来的」两者在认知科学上是完全不同的过程。前者是编码记忆看到题目、想起题解思路、把思路翻译成代码后者是提取记忆只有题目、没有参考大脑被迫重新组织知识。提取的难度远高于编码但只有经历过提取的训练面试时面对新题才能快速反应。所以两个月的间隔不是随便定的。太短比如一周记忆还没消退重写等于默写太长比如半年知识树可能已经断根。两个月是一个折中点遗忘已经发生但底层框架还在这时候重写最痛苦也最有效。这也是为什么我在第二节就强调顺序——没有前两个月的沉淀最后一步的「重写」根本无从谈起。3. 第一遍学习代码的正确打开方式题解不是用来抄的第一遍学习最容易踩的坑是把题解当答案抄。抄一遍的即时反馈很强代码跑通了、信心有了但两周后原题换个问法就不认识。第一遍学习的核心目标不是「做出这道题」而是「建立这道题对应的解题模板」并且让模板在脑内形成索引。这意味着读题解的方式、写代码的留痕方式、判断学会的方式都要重新设计。3.1 读题解的三遍法先思路、再代码、最后合上写拿到一道题不要直接看题解代码。先把题解的文字思路部分读一遍关掉编辑器自己在纸上画一下数据流想清楚输入从函数入口进来之后会经过哪些变换。这一步是「思路预演」。然后才是读代码读的时候盯着两个东西一是每一个变量的生命周期——它什么时候被创建、什么时候被更新、什么时候被丢弃二是循环或递归的终止条件——为什么是while cur而不是while cur.next。最后合上题解凭记忆写第一遍代码。写不出来没关系卡住的地方就是你的知识缺口回头再看题解把缺口补上。这套三遍法把「看题解」从一次被动接收改成了三次主动加工。第二次和第三次之间隔 10~15 分钟让大脑做一次短暂的遗忘你会发现那些真正理解的部分能写出来没理解的部分原形毕露。3.2 第一遍代码的留痕写法通过不是目的注释才是第一遍学习的代码不能只追求通过必须在代码里留下思考痕迹。我一般会在每道题的第一遍代码里写两行注释一行是「核心思路」一行是「这题的坑」。下面以剑指 Offer 里高频出现的反转链表为例展示第一遍代码应该长什么样。# 反转链表剑指 Offer 24 # 核心思路三个指针 pre/cur/nxt 沿着链表走一遍 # 这题的坑先保存 nxt再改 cur.next顺序反了会丢链表 def reverse_list(head): pre None cur head while cur: nxt cur.next # 先保存下一个节点 cur.next pre # 当前节点指向前一个 pre cur # pre 前移 cur nxt # cur 前移 return pre代码本身只有十行但每一行都不是废话。nxt cur.next必须在cur.next pre之前因为一旦修改了cur.next原来的下一个节点就找不回来了链表在这里断掉。pre的初始值是None这是链表反转后的新尾节点必须指向的终点。循环结束后pre恰好停在原链表的最后一个节点上所以返回pre而不是返回cur——cur此时已经是None了。第一遍写代码允许自己写得不优雅甚至允许暴力解。比如这题如果第一遍没想到三指针法写一个先遍历存数组再反向建链表的版本完全可以。关键是代码里那两个注释必须写清楚你怎么想的、你在哪踩坑了。这两个注释才是两个月后重写时对照的「考官」。3.3 怎么判断这题真学会了三道自测题每刷完一题用三个问题自测答不出来就标记为「待重刷」。第一不看任何资料能不能在纸上画出代码的数据流图不是背出代码而是知道每一个指针在每一步指向哪里。第二能不能说出这题的时间复杂度和空间复杂度并且说出为什么只说得出「O(n)」不够还得说清 n 是什么、额外空间用在了哪。第三能不能改一个条件立刻接上思路比如反转链表改成「k 个一组反转」你的解题思路还成立吗这三道自测题分别测的是理解、分析和迁移任何一个不过关都说明这道题你只是「见过」而不是「学会」。有些题确实难比如动态规划类的经典题第一次做只能背下转移方程。这种情况允许标记为「理解不深重写时重点看」但必须在代码注释里写明「这题当时没想通重写时要重新推导」。诚实标注比假装学会重要得多它决定了两个月后你能从重写中得到什么。4. 两个月后复习重写遗忘不是 bug是复习的入口两个月的间隔期里你会忘掉大部分题的细节解法。这是好事。如果两个月后你还能一字不差写出第一遍的代码说明第一遍只是机械记忆没有形成可迁移的能力如果写不出来说明遗忘已经帮你把「看过题解才懂的部分」过滤掉了剩下的才是真正长在脑子里的东西。复习重写的目标就是在这个遗忘的废墟上重新盖楼但这次没有脚手架。4.1 为什么「重新实现」比「再看一遍」有效再读一遍题解大脑会给你「我懂了」的错觉——因为熟悉的文字再次出现流畅度带来安全感。但重写一遍完全不同它强迫你从零开始组织代码结构定义变量、考虑边界、处理异常。这个过程在认知科学里叫提取练习被大量研究证实比重复阅读更有效。两个月后的重写本质是把第一遍学习时建立但已经模糊的模板重新激活并加固一遍。我见过太多人复习时只看题解看完觉得自己会了上了笔试考场才发现连ListNode的定义都要想半天。原因很简单看是输入写是输出面试和笔试考的都是输出能力。重写就是在模拟真实的输出场景。4.2 重写时的对比方法旧代码当考官新代码当考生重写的时候不要看着第一遍的代码写那样又变成了默写。先把第一遍的代码藏起来只看题目从零开始写。写完之后再打开旧代码对比这个对比过程才是复习的核心。# 第二遍重写反转链表递归版 # 核心思路先反转后面的子链表再把当前节点接到末尾 # 这题第二遍换写法验证自己对递归的自顶向下理解 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 return new_head这版递归写法和第一遍的迭代写法表面上是两种实现本质上是对同一道题的两种抽象方式。迭代版强调过程控制一步步把指针掰过来递归版强调结构归纳先假设后面的已经反转好再处理当前节点。第二遍故意换一种写法不是为了炫技而是为了验证第一遍建立的思路是否完整。如果递归版能独立写出来说明你真正理解了链表反转的递归结构而不是背下了迭代模板。对比新旧代码时重点看三个地方新代码和旧代码的差异点在哪里、新代码犯的错误是不是第一遍踩过的坑、新代码能不能处理比旧代码更多的边界情况。比如递归版这里有个隐藏问题——如果传入的是空链表head is None条件直接返回None所以空链表安全。如果调用方传入只有单个节点的链表head.next是None也直接返回。这两个边界在第一遍学习时可能根本没想过第二遍写递归时必须主动验证。4.3 重写的评分三条线时间、调试、讲解重写完成后给自己打三个分数。第一从看到题目到写完代码的时间。剑指 Offer 级别的题两个月后重写应该在 15 分钟内完成超过 30 分钟说明第一遍没学好标记为「三刷」。第二调试次数。写完直接跑通最好如果调试超过三次说明你对边界条件的记忆是模糊的需要回头看第一遍的「坑」注释。第三也是最重要的能不能不看着代码用大白话给别人讲清楚这题的做法。讲得出来才叫真的会讲不出来就是还没形成结构化记忆。这三个评分标准前两个量化、第三个定性。它们共同回答一个问题这题通过重写从「第一遍的看过」升级到「第二遍的会做」了吗如果没通过别急着往前走当天就把这题放进「三刷清单」隔一周再重写一次。这套方案的设计里重写不是终点只是另一轮学习的起点。5. 真题刷法与避坑现场五个翻车场景一次排干净复习重写完成之后最后一步是大公司笔试真题。这个环节的定位是「模拟」不是「学习」。它检验的是前两轮刷题的实际效果顺带让你熟悉真实笔试的题型分布、时间压力和输入输出格式。但真题环节的陷阱比想象中多很多人不是不会做题而是被各种环境问题、习惯问题绊倒。5.1 真题的正确打开时间复习完成之后再碰真题不要在第一轮就做。理由很简单真题考察的是综合能力一道题可能同时涉及数据结构、贪心思路和边界处理第一轮的你知识树还没长全做真题大概率被按在地上摩擦不但没有训练效果还会打击信心。正确做法是等「两个月后复习重写」全部完成所有题都过了第二遍再开始做真题。这时候做题的目的不是查漏补缺而是体检——看自己在真实笔试的压力下能发挥出几成实力。5.2 别按公司刷按知识点聚类刷很多人打开真题文件习惯按公司分类先做甲公司的历年题、再做乙公司的。这不是最优策略。大公司出题有很强的共性与其按公司刷不如按知识点聚类把真题里所有二分查找相关的题放一起刷所有动态规划相关的放一起刷。这样做的原因是面试官出题时也是按知识点出题同一知识点的不同变体能帮你把一个算法吃透而不是被不同公司的出题风格带着走。按知识点刷还有一个额外收益你很快会发现哪些知识点是高频考点哪些是冷门内容。比如链表、二叉树、动态规划几乎必出而某些偏门数据结构可能只在个别年份出现。这个发现反过来能指导你最后一周的复习重心——高频考点再快速过一遍冷门内容看一眼思路就行。5.3 笔试环节五个高频翻车现场这一节是血泪经验每一条都是真实笔试里反复出现的坑。翻车一本地跑通提交编译不过。现象代码在自己的 IDE 里运行正常一提交到在线评测系统就报编译错误。原因本地环境自动引入了某些包或头文件而在线系统是干净环境或者你的代码依赖了本地特有的配置。解决第一遍学习时就养成不依赖任何外部包的习惯纯标准库实现提交前检查代码里有没有隐式依赖比如 C 是否漏了#include。这道题跑不通不是你算法不对是环境黑匣子没摸清。翻车二题目读到一半就写写完发现理解错。现象题目要求返回索引位置你返回了值题目说可能有重复元素你没处理。原因笔试时间压力下大脑急于开始写代码阅读阶段被压缩了。解决逼自己在草稿纸上写三行「输入 → 处理 → 输出」的流程再动笔写完用题目给的示例手动走一遍确认没有歧义再写代码。多花两分钟读题能省下二十分钟返工。翻车三一上来就追求最优解结果暴力解都没写完。现象看到题目第一反应是 O(n) 的优化思路想了十分钟没想出来最后连 O(n²) 的暴力解都没时间写完。原因把笔试当面试以为只有最优解才能过。解决真实笔试中暴力解能帮你拿到部分分空题是零分。拿到题先写暴力解保底再在暴力解基础上优化。这个习惯需要在第三轮模拟笔试时就刻意练习。翻车四常见 API 记不熟写代码像挤牙膏。现象排序函数的参数、栈和队列的初始化写法要想很久。原因第一轮刷题时过度关注算法思路忽略了标准库的熟练度。解决在复习重写阶段强制自己把所有常用数据结构的标准库调用写一遍不要复制粘贴。这条没有什么技巧就是笨功夫但它是从「想得出来」到「写得出来」之间的最后一公里。翻车五自我感觉良好提交全是超时。现象样例通过数据一大就超时。原因笔试数据的规模比样例大得多你的解法虽然正确但复杂度不够。解决每道题写完先口算一下时间复杂度如果接近 10^8 次运算就得考虑优化。笔试题库的数据范围通常会在题目里给出读题时圈出来——n 是 10^3 还是 10^5直接决定了你该写 O(n²) 还是 O(n log n)。6. 验证这套刷题方案真成了三个指标加一个可执行的复盘习惯三个月走完三轮怎么知道这套方案在你身上真的起了作用我在带新人时一般用三个指标做验收。第一随机从剑指 Offer 里抽 10 道题每道题从读题到写完代码控制在 15 分钟内且一次性通过率不低于 7 成。第二在牛客或同类平台按公司真题模拟笔试分数稳定在模拟面试通过线以上。第三答一道稍作变形的题——比如原题改了数据范围或增加了一个约束条件——你不需要翻资料就能在思路上反应过来。这三个指标分别对应速度、稳定性和迁移能力三者同时达标才算真正吃透了这套资料。还有一个我个人的习惯推荐给你每次复习重写完成一道题就打开 Git 在你自己的刷题仓库里提交一次commit message 写上这题的核心思路和这次踩的坑。三个月后回头看提交历史你会看到一条清晰的成长曲线——第一遍的代码注释里全是「这题不会、勉强写出来」第二遍的注释里变成了「这题有两种解法我写了递归版」第三遍的真题模拟记录里已经能看到时间分和正确率。这份提交历史就是你有没有真正刷懂的最诚实的证据。我自己带过的同学里最典型的失败案例不是不努力而是第一遍刷题时抄题解抄得太顺两个月后重写时原形毕露心态崩了直接放弃。如果你也在某个节点遇到这种挫败感记住一件事重写时写不出来才是正常的这个「写不出来」的时刻正是方案设计里的预期动作不是你的失败。撑过这个阶段真题环节会给你一个明确的反馈。希望这套方法论对你刷题有用走完一遍再回头看你会感谢那个逼着重写第二遍的自己。本文还有配套的精品资源点击获取
返回列表