代码随想录刷到第67天说实话这一天比我想象中来得平静。没有“终于结束了”的解脱感也没有“我全都学会了”的兴奋更多的是一种踏实的收束感。从第一天的数组二分查找开始到后来二叉树、回溯、动规、单调栈再到最近这段时间的图论一路跟着这套路线走下来最大的变化不是AC了多少题而是拿到一道陌生题目时不再慌张心里会自然浮现出“这题大概属于哪个模块、可能用什么思路”的框架感。这篇总结既是给这段刷题旅程做一个阶段性的交代也希望能给正在刷到中后期、或者正准备开始的朋友一些参照。如果你正卡在某个模块觉得进度慢或者刷完一遍觉得什么都没记住这篇内容应该能帮到你。我会把第67天这个节点上真正沉淀下来的东西拆开讲路线覆盖了哪些内容、每个模块的核心模板是什么、哪些坑我反复踩过、以及最后阶段我是怎么把刷题转化成面试能力的。1. 从day1到day67这条刷题路线到底覆盖了什么1.1 为什么第67天值得写一篇总结先说一个很多人会问的问题67天到底算快还是算慢我的感受是这个时长对大多数在职或者在校、每天还要上课/上班的人来说是一个比较可持续的节奏。我自己的计划是每天一到两道新题搭配复习前一天的内容周末会多花一点时间整理错题和写总结。这样算下来一天在算法上的投入大约一个半小时到两个小时不会挤占到正常的工作和生活又能保证手感不断线。day67这个节点之所以值得停下来写点东西是因为它恰好卡在路线的尾部。前面那些天你是在“跟随输入”——看题解、写模板、模仿思路从第67天开始更重要的能力变成了“独立输出”——给你一道没见过的题能不能自己分析出思路、写出代码、讲清楚复杂度。这个转变不是自动发生的需要主动做一次复盘。写总结本身就是复盘它会逼你把散落在不同章节的知识点串成一张网。还有一点很多人刷题到后期会有一种“学了后面的忘了前面的”的焦虑。第67天回过头去看你会发现这种焦虑其实是因为没有建立知识之间的联系。当你把数组、链表、哈希、树、图这些模块放在一起看会发现它们是有明确递进关系的。这个章节想做的就是把这张地图展开。1.2 全局视角从数组到图论的知识版图先把这套路线覆盖的内容粗略拉一遍。我不会事无巨细地列每一道题而是按模块整理出主干方便你对照自己的进度。模块覆盖的核心考点我的掌握评级数组/字符串二分查找、双指针、滑动窗口、螺旋遍历、KMP掌握较好链表虚拟头节点、反转、环检测、合并有序链表掌握较好哈希表Set/Map选型、三数之和、四数之和掌握较好栈与队列括号匹配、单调队列、优先队列、单调栈需复习二叉树递归三要素、迭代遍历、属性计算、BST、公共祖先掌握较好回溯组合、切割、子集、排列、棋盘问题掌握较好贪心区间问题、跳跃游戏、分发问题、单调递增数字需复习动态规划背包、打家劫舍、股票、子序列、编辑距离核心掌握图论DFS/BFS、并查集、最小生成树、拓扑排序、最短路刚学完需巩固从这张表里能看出一条清晰的主线数据结构从线性数组、链表、栈队列到树形二叉树再到图算法的思考方式从暴力枚举到递归回溯再到动态规划的“状态转移”最后是贪心和图论里的各种经典策略。到了day67整个版图其实是完整的。这里多说一句很多人纠结要不要把每个模块都刷得特别深再进下一个模块。我的建议是不要。路线设计本身是合理的第一遍追求的是“见过这些题型、知道模板长什么样”深度可以留给二刷三刷。如果你在某个模块卡了太久很容易导致后面图论这种大模块时间不够。2. 每个模块真正沉淀下来的核心模板2.1 二叉树与回溯所有递归问题的共同骨架刷到第67天回头看二叉树和回溯其实是整套路线里性价比最高的两个章节。为什么因为它们是递归思维的集大成者后面的图论很多DFS思路本质上就是二叉树遍历的扩展。理解了这一层图论里的深度优先搜索就不会觉得是新东西。二叉树的递归遍历核心就是三件事这个递归函数返回什么、终止条件是什么、本层要处理什么。前中后序的区别只是访问根节点的时机不同。迭代遍历我后来也总结出了一套统一写法用栈加一个标记位把“处理节点”和“访问节点”分开代码风格统一不容易记混。回溯问题就更依赖模板。组合、切割、子集、排列、棋盘问题都可以套同一个框架def backtrack(路径, 选择列表): if 满足结束条件: 记录结果 return for 选择 in 选择列表: 做选择 backtrack(路径, 新的选择列表) 撤销选择关键区别在于“选择列表”怎么维护。组合问题用startIndex控制排列问题用used数组标记已经用过的元素子集问题每个节点都要记录结果。这个模板背熟之后遇到“全排列”还是“组合总和”其实就是在改一行代码。等到刷图论的时候你会发现岛屿问题、单词搜索这类题目本质上就是回溯模板在二维网格上的应用。所以我会说二叉树和回溯是后面所有递归问题的地基。2.2 动态规划五部曲从状态定义到滚动数组优化动态规划是整套路线里耗时最长、也最容易让人心态崩的部分。但到了day67再回看它其实是套路感最强的一个模块。代码随想录里反复强调的五步法我后来把它变成了自己的解题习惯定义dp数组的含义、写递推公式、想清楚初始化、确定遍历顺序、打印dp数组验证。前两步是重中之重也是大多数人卡壳的地方。dp数组定义得好不好直接决定递推公式能不能写出来。比如背包问题如果你只是机械地背“先遍历物品再遍历背包”而不知道为什么遇到变体题就会懵。0-1背包的一维数组写法内层循环必须倒序遍历是为了保证每个物品只被选一次完全背包内层正序遍历是因为物品可以重复用。这个差异不是死记的你把二维dp数组的状态转移画出来就明白了。股票问题是我觉得最有意思的系列。买一次卖一次、可以多次交易、带冷冻期、带手续费看起来变化很多其实核心就是把每一天的状态拆成“持有”和“不持有”。状态多了就用二维数组每一维表示一种状态。掌握了这个思路来一道没见过的股票变体题也能自己推出来。我踩过的坑主要在两个地方一是初始化比如背包dp[0]的含义没想清楚导致结果整体偏移二是遍历顺序纯0-1背包和纯完全背包搞混后来我把几种背包问题的手写递推过程在纸上练了好几遍才算彻底分清。2.3 单调栈与图论模板最后阶段补上的两块拼图单调栈是路线里比较靠后的章节它解决的问题非常明确在一维数组中找左边或右边第一个比自己大/小的元素。暴力解法是O(n²)单调栈可以做到O(n)。它的核心就是维护一个栈保证栈内元素单调递增或递减每次弹出的时候就是被弹出元素的答案确定的时刻。这个“答案在弹出时确定”的理解方式比背代码更重要。接雨水和柱状图最大矩形这两道题把单调栈的用法演得明明白白。图论是这次学到的新内容而且内容量真的不小。DFS和BFS比较容易上手核心是处理visited数组避免重复访问。并查集模板我觉得非常值得背下来因为它能解决很多看似复杂的问题比如判断两个节点是否连通、统计连通分量数量而且代码非常短class UnionFind: def __init__(self, n): self.parent list(range(n)) self.rank [0] * n def find(self, x): if self.parent[x] ! x: self.parent[x] self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union(self, x, y): rx, ry self.find(x), self.find(y) if rx ! ry: if self.rank[rx] self.rank[ry]: rx, ry ry, rx self.parent[ry] rx if self.rank[rx] self.rank[ry]: self.rank[rx] 1最小生成树我用的是Kruskal算法按边权排序后一条条加入并查集理解起来很直观。拓扑排序用队列维护入度为0的节点代码也不复杂。最短路径的Dijkstra和Floyd我就没有深挖到每一行代码都能手写的地步先做到知道原理、能写模板后面再有针对性地补。3. 刷题过程中反复踩到的坑和纠偏记录3.1 代码能过但讲不清楚面试表达与刷题的脱节刷题过程中最大的一个坎不是做不出题而是做出来了但讲不明白。有段时间我特别追求AC数量每天刷完就扔觉得“这题我会了”。结果有一次和朋友互相模拟面试让我讲讲最近做的一道题时我发现我只会说“用双指针”但具体为什么用双指针、指针移动的边界条件是什么、复杂度为什么是O(n)说得支离破碎。那次经历让我意识到刷题和面试是两种能力。刷题是“看懂题解→自己写出来”面试是“现场分析问题→讲清思路→写出代码→分析复杂度→应对追问”。后者需要你主动训练。后来的纠偏方法是每道题做完强制自己写三句话。第一句暴力解是什么复杂度多少。第二句当前解法为什么更优核心优化点在哪里。第三句边界条件有哪些最容易错的地方是什么。不需要写长文三句话就行但必须写出来。这个习惯坚持下来效果非常明显到后期很多题看一眼就能在心里把这三句话过一遍。3.2 重复刷题的边界二刷不是背答案另一个常见的坑是二刷变“背答案”。题目做过一遍第二遍看到题号手指比脑子先动直接把之前的代码敲出来了。这种复习没什么价值因为它没有激活你的思考过程。我纠正的方式是二刷时坚决不看之前的代码。先只看题号在心里回忆思路想不起来的再回去看原始题解。而且二刷的重点不是“这题能做出来”而是“这题能不能用比第一遍更清晰的方式讲出来”。如果第二遍还是卡在同一个地方那就要把这个点记进错题本说明这块是你的思维盲区不是再看一遍题解就能解决的。还有一个经验二刷的间隔要有梯度。第一遍刷完隔一周复习一遍再过两周复习第二遍已经比较熟练的题一个月后再拉出来过一遍。这样比连续几天反复刷同一道题有效得多。3.3 时间管理一天几题才算合理关于刷题量我一直认为可持续比冲刺重要。周末看别人一天刷十道题很容易焦虑。但刷题是马拉松第67天回头看日拱一卒才是王道。我的节奏比较简单工作日中午休息和晚上各抽一点时间总共大概一个半小时。新题只做一道另一道是复习前一天或者前一周的题。周末两天各花两到三小时做两道新题剩下的时间整理本周错题和写模板。遇到状态特别差、一道题怎么看都看不懂的时候我不会死磕超过一个半小时会把题先标记好跳过第二天再回来。现在回看这种节奏让我在day30左右迎来过一次明显的“质变”体验——很多题开始觉得有共同点不再是一道一道孤立的题而是一类一类的套路。如果你现在还在往前刷别急着赶进度保持住节奏质变会来的。4. 收官阶段的复习清单与实战建议4.1 按模块整理的个人错题本到第67天我最重要的资产不是AC记录而是一份按模块整理的错题本。它不是为了记“我错在哪”而是为了回答一个问题“我卡住的题背后是哪种思维漏洞”我的错题本表头是这样的题号模块错因类型核心收获示例题A动态规划遍历顺序写反完全背包内层正序0-1背包倒序示例题B图论忘记visited标记DFS/BFS必须防止重复访问示例题C回溯剪枝条件判断错误先排序再剪枝效率更高错因类型我大致分了几类边界条件、初始化错误、遍历顺序、递归出口、思路偏差。整理到最后你会发现大多数错误类型是重复出现的。比如我自己“边界条件”这一类就最多尤其是数组越界和空值判断说明每次写循环前都应该先想清楚区间的开闭。最后阶段还有一个复习技巧不要只看错题内容要对着模块清单做一次“盲测”。随便拿一张纸写下每个模块你记得的模板和典型题型再看笔记补充遗漏。这个动作能帮你发现哪些模块只是“刷过”而没“记住”。4.2 从刷题到面试的最后一公里刷题刷到最后终究要落到真实的面试场景。第67天开始我调整了练习方式。之前都是在IDE里写代码有语法高亮、有自动补全但面试通常是共享屏幕或者白板。所以后来我大部分新题都是在纯文本编辑器里写的不装任何代码辅助插件语法错误全靠自己看出来。这个转换比你想象中痛苦建议尽早开始。时间分配上我给自己定了一个固定流程审题三分钟想清楚输入输出的边界写代码控制在十分钟左右写完再花五分钟从头到尾读一遍代码自己走一遍例子检查逻辑漏洞。严格卡时间练几次之后速度和准确率都会有明显提升。至于项目和算法的时间比例我个人觉得如果目标岗位偏后端/客户端算法不能丢但也不要占用全部时间。到后期我每天给算法的时间压缩到了一个半小时以内剩下时间用来准备项目和八股。算法能力到这个阶段每天维持手感即可重点从“增量刷题”转向“存量巩固”。最后再分享一个小细节。我在day67这天做的第一件事是把自己整理的十几张模板纸用A4纸打印了出来从二叉树的迭代遍历到并查集一张一张过。很多内容其实已经记住了但手写在纸上会暴露出那些“以为自己知道实际写不出来”的空白点。这个笨方法比再多刷十道题都让我踏实。