第十五周刷题总结:从能做出来到讲得清楚
2026/9/9 22:12:35 网站建设 项目流程

第十五周刷题总结:从“能做出来”到“讲得清楚”

刷题这件事,第十五周是个很有意思的节点。前面三个月的新鲜感早就褪了,肌肉记忆开始形成,但瓶颈感也在同一时间冒出来:题量上去了,看到题却总觉得“见过但不会做”。我自己的第十五周刷题总结,主线不再纯粹是“又刷了多少道”,而是从题型分类、套路沉淀、复述检验三个维度做了一轮系统性的复盘。这篇文章就是这周复盘的全记录,包含了我对选题思路、典型套路、踩坑过程的具体拆解,适合正在按周推进刷题计划的人,也适合准备面试算法题时总感觉差一口气的人。

这周我一共刷了42道题,其中新题28道,二刷及以上14道,集中在数组、二叉树、动态规划三个板块,外加每天两道热题保持手感。数据不算夸张,但重新组织和输出的内容比之前四周都要扎实。因为到第十五周,我开始强制自己把每道题不只是“跑通”,而是能用一两句话讲清楚“为什么这么解”。这个转变带来的效果非常明显:以前很多一知半解的题,现在能自己推出来;以前每次都要翻答案的模板题,这周开始能够独立默写核心代码。

1. 第十五周刷题概况与选题思路

1.1 本周核心范围与数据回顾

按照老规矩,每周的第一天先定范围。第十五周我没有继续开新专题,而是做了两件事:第一,把前几周刷过的区间问题、二叉树遍历、基础动态规划做了一次横向整理;第二,每个分类挑出高频题和易错题,重新做一遍并补充了完整注释。

本周记录如下:

  • 新题数量:28道,其中数组类8道、二叉树类9道、动态规划类7道、其他(栈、哈希、双指针)4道
  • 二刷及复现:14道,全部要求“不看答案独立完成,并输出题解思路”
  • 周中错题复刷率:100%,每次错题都记录到了错题本,并在三天后重新写一遍核心代码
  • 累计耗时:约21小时,平均每天3小时,周末会额外加一场两小时的完整模拟

这个数据放在刷题大军里根本不算多,但第十五周我更在意的是“吃透率”。所谓吃透,我给自己定的标准是:过两周再看这道题,不假装熟悉,能直接说出考点、边界条件和最优解推导路径。这个标准定下来之后,刷题节奏明显慢了下来,但踏实了很多。

1.2 周计划安排与选题策略

很多人在周计划阶段容易犯一个错误:按题目难度排期,看到中等题就跳,看到困难题就发怵,结果一周下来全是简单题,或者全是同一个类型的题。我把第十五周的安排分成三个部分:

  • 周一至周三:集中处理“旧题复盘 + 同类新题扩展”,比如二叉树部分,先看前两周做过的前序、中序、后序遍历,再延伸到层次遍历、之字形遍历、最近公共祖先。
  • 周四至周五:主攻动态规划的“状态定义训练”,不刻意追求难题,而是把爬楼梯、打家劫舍、最长递增子序列、不同路径这几类常见模型反复对比,搞清楚为什么有的题一维dp就够了,有的必须二维。
  • 周末:做两场限时练习,一场用来模拟真实笔试环境,一场用来输出题解,写给别人听。

这个策略的核心逻辑是:每周一个主题,围绕主题找变体,而不是每天换一个完全不同的专题。从第十五周的效果看,对比之前碎片化刷题,正确率提升了大概20%左右,尤其是看到题目后的第一反应,明显从“这是什么题”变成了“这题属于哪个模型的变体”。

2. 本周吃透率最高的三类题型套路

2.1 区间类问题:从暴力解法到差分数组的思考链

这周数组部分我重新梳理了区间问题。区间问题在面试里出现频率极高,但真正难的不是判断边界,而是选择数据结构。

以经典的“会议室问题”(给定一系列会议时间区间,判断能否参加所有会议)为例。最暴力的做法是两两比较区间是否有重叠,时间复杂度O(n^2)。但稍微观察一下就会发现:如果先把所有区间按开始时间排序,再检查相邻两个区间是否有重叠,一次遍历就够了,排序O(n log n),检查O(n),总复杂度O(n log n)。

这就是区间类问题第一个核心思想:排序后用相邻关系替代两两比较。

接着我把它引申到“合并区间”“插入区间”“会议室II(需要多少间会议室)”这三道变体题。合并区间需要在排序后维护当前覆盖范围,能合并就合并,不能合并就输出上一段;会议室II的常见思路则要换一个角度,把开始时间和结束时间分别排序,然后模拟指针推进。

第十五周我在这个分类里学到了一个以前没仔细用的工具——差分数组。对于“给定多个区间,计算每个点的最大覆盖次数”这类问题,差分数组可以直接把区间更新的时间复杂度从O(k)降到O(1),这个思路在很多题里都能迁移。日常理解的话,把差分数组想成“一个本子,每个区间的开始位置记一个+1,结束位置后一个位置记一个-1”,最后从前到后累加,就能知道每个点被多少个区间覆盖。这个方法在“航班预订统计”里几乎是标准解法。

2.2 二叉树遍历模板:一个核心框架吃透迭代与递归

二叉树题在第十五周的复盘中占了最大比重,因为我发现自己过去对遍历的掌握还停留在“递归套模板”的层面,一旦面试官要求写迭代写法就容易卡壳。

先说递归。二叉树前序、中序、后序遍历的代码只有几行,本质上都是在访问当前节点这件事上做文章:

void dfs(TreeNode* root) { if (!root) return; // 前序位置 dfs(root->left); // 中序位置 dfs(root->right); // 后序位置 }

关键是理解这三个位置到底意味着什么:前序位置是在进入节点时立刻处理,中序位置是在处理完左子树之后、右子树之前,后序位置是在左右子树都处理完返回时。很多树的题目,思考“应该在哪个位置写处理逻辑”就能迎刃而解。

但递归的问题在于,一旦树很深或者面试官追问,栈溢出和迭代实现就是绕不开的点。这周我把迭代写法彻底练了一遍。核心是用一个显式的栈模拟递归过程。前序遍历最简单,每弹出一个节点就处理,然后先压右孩子再压左孩子;中序遍历需要一个指针变量,先一路向左压栈,再逐个弹出处理,并转向右子树;后序遍历则可以用“前序遍历的镜像”来做,先压左孩子再压右孩子,得到“根-右-左”的顺序,最后反转结果,就得到“左-右-根”。

还有层次遍历,本质上就是BFS,用队列。模板代码如下:

vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; if (!root) return res; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int sz = q.size(); vector<int> level; for (int i = 0; i < sz; i++) { TreeNode* node = q.front(); q.pop(); level.push_back(node->val); if (node->left) q.push(node->left); if (node->right) q.push(node->right); } res.push_back(level); } return res; }

这个for循环次数等于当前队列大小的写法,是层次遍历区分“每一层”的关键。这周我在“二叉树的右视图”“填充每个节点的下一个右侧节点指针”“二叉树的最大宽度”三道题里都用到了这个模板,只是每层的处理逻辑稍作变化,整体结构完全一致。

2.3 动态规划的状态定义与转移方程推导

动态规划一直是很多人的老大难,第十五周我也不例外。但这一轮复盘的收获在于,我终于总结出了一套相对稳定的推导流程:

第一步,先想清楚dp[i]或者dp[i][j]代表什么含义,这是最重要的,状态定义错了后面全错。第二步,找转移关系,思考dp[i]是怎么从dp[i-1]、dp[i-2]或者更早的状态得到的。第三步,确定初始条件和边界。第四步,返回目标状态。

以“最长有效括号”为例。题目给一个只包含'('和')'的字符串,求最长有效括号子串长度。暴力做法是每个起点枚举,每次判断合法性,O(n^2)。dp做法就优雅很多:定义dp[i]为以第i个字符结尾的最长有效括号长度。如果s[i]是左括号,那么它不可能作为有效子串的结尾,dp[i]=0。如果s[i]是右括号,就需要分情况:当s[i-1]是左括号,它俩配成一对,dp[i] = dp[i-2] + 2;当s[i-1]也是右括号,就要看i-dp[i-1]-1这个位置是否是左括号,如果匹配,dp[i] = dp[i-1] + 2 + dp[i - dp[i-1] - 2]。

这个推导过程思路本身不复杂,但如果状态定义不够清晰,很容易糊涂。我的建议是不要硬背转移方程,而是画一个字符串索引的示意图,把“以i结尾”这个边界条件反复标注清楚。第十五周我在白纸上把最长有效括号、最长回文子串、编辑距离这三道经典题从头到尾推导了一遍,做完之后对“状态从哪来”有了明显更深的体感。

3. 复盘方法论:让做过的题变成自己的武器

3.1 一刷二刷三刷的时间节奏

第十五周最大的变化是,我建立了更明确的刷题节奏:第一次刷题,只求理解思路,可以看题解,但看完后要关掉答案自己写一遍;三天后第二次刷,独立完成,重点检查有没有卡壳;两周后第三次刷,限时十分钟出思路,二十分钟调试完成。

这个节奏的好处是,它把“会不会做”和“能不能记住”分开了。很多人刷题遇到的问题是:当时看懂了答案,觉得简单,过两周连题目讲什么都忘了。这就是缺少间隔重复的后果。记忆曲线的原理大家都知道,但落到刷题里,很多人就是不愿意回头做旧题。第十五周我强制自己把二刷率维持在30%以上,效果立竿见影,面试风格的限时练习里,读题后想思路的时间明显短了。

3.2 错题本的正确记录姿势

之前我记错题本的方式是:把题号、题名、题解粘进去,美其名曰整理,实际上不会再看第二遍。这周我换了方式,每道错题只记录三样东西:

  • 错误原因:是边界条件漏了,还是转移方程写错,还是数据结构选错
  • 思考卡点:卡在哪一步,为什么没想到这一步
  • 正确思路的一句话版本:方便三天后自我提问

比如有一道“乘积最大子数组”,我犯的错误是只维护了当前最大乘积,却忽略了负数乘负数可能变成最大值的场景。我当时的错误原因记录是“只维护了最大状态,没有维护最小状态”。三天后再看,一眼就想起正负切换这个关键点。

错题的记录不需要长,但要直击痛点。我见过很多人错题本写得很漂亮,各种颜色的高亮,但核心信息都被淹没在排版里。刷题笔记是给自己看的,不是手账,简洁、能回忆起当时的坑就好。

3.3 白纸复述和讲题输出

这周我试了一个新方法,每天挑一道题,按“题目目标-核心思路-复杂度分析-边界情况”四步在白纸上复述,不是写代码,而是像面试时讲思路一样说出来。

这个方法远比想象中有效。因为“写代码”可以依赖调试器修正,但“讲思路”不行,思路不清晰当场就卡住。比如讲“盛最多水的容器”这道题,我一开始只能说出来“双指针移动”,但问到为什么移动较短的边,就含糊了。后来想明白了:容器高度由短板决定,移动短板可能找到更高的新板,移动长板高度只会更差。这一句话想清楚之后,双指针就不是背下来的,而是推出来的。

第十五周这个“讲题”动作大概花了每天二十分钟,但对长期记忆的帮助,比多刷十道新题还大。

3.4 模板代码沉淀与分类整理

随着刷题量增加,我建了一份自己的“模板库”文档,按数据结构分类,每类收录两到三个核心模板。这份模板不是从网上复制粘贴的大而全版本,而是自己手写并注释过的精简版。

比如二分查找模板,我记录了自己最常用的左闭右开写法:

int lower_bound(vector<int>& nums, int target) { int left = 0, right = nums.size(); // 左闭右开 while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] < target) left = mid + 1; else right = mid; } return left; }

关键注释只有一句:“最终left指向第一个不小于target的位置”。这个版本避免了死循环,也统一了搜索区间。模板的意义不是让你死记硬背,而是把每一类题里共通的骨架提炼出来,遇到变体题时只需要在该变的地方变。

4. 本周踩过的坑与排查技巧实录

4.1 TLE超时问题:从暴力到优化的实战记录

第十五周我在“和为K的子数组”这道题上交过一笔昂贵的学费。最直接的思路是枚举所有子数组并求和,O(n^3),心想数据范围小,说不定能过,结果一提交直接超时。

第二次优化成前缀和数组,预处理前缀和数组之后,枚举子数组起点和终点,O(n^2)可算,但在一组大数据上还是超时。这时候我才意识到,这题的正确打开方式是“前缀和 + 哈希表”。

核心思路是:子数组和等于k,等价于前缀和数组中,当前前缀和减去目标k之后的值,在之前出现过。用一个哈希表记录每个前缀和出现的次数,一边遍历一边查询,时间复杂度就降到了O(n)。

int subarraySum(vector<int>& nums, int k) { unordered_map<int, int> mp; mp[0] = 1; int sum = 0, res = 0; for (int num : nums) { sum += num; if (mp.count(sum - k)) res += mp[sum - k]; mp[sum]++; } return res; }

这个坑给我的教训很直接:不要用“先暴力再优化”的思路应对有明确时间限制的题目。如果题目涉及“连续子数组”并且需要统计次数,应该第一时间往“前缀和 + 哈希表”这个组合想。暴力保平安在小数据量下是对的,但在竞赛和面试场景下,通常目标解法就是最优解附近。

4.2 边界条件与数组越界复盘

第十五周的错题里,有接近三分之一的错误源自身边界条件没处理好。

一个典型案例是“最长回文子串”。我用中心扩展法实现,逻辑本身不复杂,但扩展的判断条件写成了 while (left >= 0 && right < n),忘记了左右指针还要满足 left <= right,导致奇数长度和偶数长度区分的时候,偶尔会出现越界访问。这种问题最坑的地方在于,小数据量测试根本测不出来,一旦n=1或者回文串出现在字符串尾部,才会暴露。

排查这类问题我的经验是:写完代码之后,立刻用三个极端用例做自测,分别是数组长度n=1、n=2、以及目标结果出现在数组最末端。这三个用例一旦全部通过,越界问题基本能堵住百分之八十。

4.3 贪心思维的常见陷阱

有一类题目会故意引导你往贪心方向想,但正确答案却需要动态规划。第十五周我踩的“跳跃游戏II”就是很好的例子。

题目要求从数组第一个位置出发,跳到最后一个位置的最少步数。一开始我试图维护“当前能到达的最远距离”,每次能跳多远就跳多远,结果在某些用例上答案不对。原因在于,局部最优不等于全局最优。这道题正确的贪心解法确实成立,但需要维护的是“当前步数能覆盖到的区间”以及“下一步能覆盖到的最远区间”,每次在可覆盖的区间内挑一个能跳得最远的位置,而不是盲目地第一次能跳多远就跳多远。

区分贪心和动态规划的一个直观标准是:如果当前选择会限制之后的选项,且需要全局考虑,通常不是贪心;如果当前选择只影响到一个局部结果,且每一步的最优能推出全局最优,才适合贪心。

4.4 心态管理:当刷题从激情变成了任务

第十五周我对心态问题有了新的体感。起因是周三晚上做一道中等难度的动态规划题,前前后后写了40分钟,提交后25个用例只过了17个,状态转移方程出问题,一时间既生气又沮丧。

过去遇到这种情况,我一般会硬刚到底,但这次我选择停下来,去楼下的便利店买瓶水,回来之后把题目重新读了三遍,画了张状态转移的示意图,问题一下就清楚了。后来我反思,刷题过程中的情绪管理,本质上和算法设计中的“剪枝”是一回事。当你在错误的方向上继续投入,边际收益是递减的,甚至为负。与其硬撑着写,不如给自己设定一个“冷静时间”:连续卡30分钟就起身走一圈,回来不直接看题解,而是把题面当新题重新思考一遍。这个方法第十五周帮我解决了不少原本可能要翻答案才能解的题。

5. 十五周刷题实践中的工具与资料取舍

5.1 刷题平台的搭配使用

我平时主要用一个主刷题平台完成日常练习,周末会额外用一个在线评测平台做限时模拟。两个平台的侧重点不一样:日常主刷题平台的题解区讨论氛围比较好,适合看别人的思路和优化方案;周末模拟平台更接近竞赛场景,时间卡得更紧,可以训练在压力下快速定位考点。

但需要注意一点:不要同时在六七个平台注册账号,每天换着刷。平台太多会导致知识碎片化,这套系统做几道,那套系统做几道,最后连自己的刷题记录都找不全。选定一个主要平台,把所有专题的题单都建立在这个平台上,历史记录、错题、收藏夹都在同一个地方,复盘时才不会到处翻。

5.2 本地笔记工具与代码管理配置

第十五周我开始把笔记从零散的备忘录迁移到本地Markdown文件,并且统一了文件命名规则:按“日期-专题-题目名”的格式存储。这个习惯的建立,让复盘时找历史笔记的效率提高了很多。

配合本地代码管理,每个专题一个目录,题解按文件名存成单独的Markdown文件,题目标题用链接指向原始题目,代码段直接嵌入。这样一份笔记就是一份可检索的题解库,配合搜索功能,查找“之前那道区间合并的题”只需要搜索关键词,而不是滚动屏幕翻记录。

5.3 资料选择的“少而精”原则

这周我清理了一批收藏从未看的算法资料和关注列表。之前总喜欢囤各种“算法模板大全”“一百天刷题计划”,实际上每份资料都只翻了前几页。第十五周之后,我的原则是:一份系统性的教材 + 一个题单网站 + 一个交流社区,其他的基本不看。

系统性教材用来建立知识框架,题单网站用来安排每周练习范围,交流社区用来在卡壳时查看别人的思路。三个渠道各有分工,信息冗余少了,专注度自然就上来了。刷题到了第十五周,真正稀缺的已经不是资料,而是对已学内容的消化能力。

6. 关于第十五周刷题总结的一些个人体会

写到这里,第十五周刷题总结也算接近尾声了。如果只让我保留一条这周最重要的心得,我会选这句话:刷题的数量远没有刷题的质量重要,而刷题质量的核心标志,是你能不能在不看答案、没有提示的情况下,把一道题的来龙去脉讲清楚。

“讲清楚”三个字看起来简单,做起来很考验人。它要求你不仅知道解法的步骤,还要知道为什么选这个解法,为什么这里的边界条件是开区间而不是闭区间,为什么这个算法的时间复杂度更优。这些问题在写代码的时候很容易被忽略,但在面试和真实工程场景里,恰恰是判断一个人是不是真的理解了问题的关键。

另一个体会是:刷题不是冲刺跑,而是一场漫长的马拉松。第十五周的我已经没有了第一周那种“今天必须刷十道题”的冲劲,日子反而变得更加平淡和规律。但这种平淡里有一种踏实的进步感。当你发现自己可以在15分钟内默写出二叉树迭代遍历的完整模板,可以在一分钟内判断一道题属于哪种模型,你会感受到前期积累的回报正在一点点显现。

最后再分享一个小技巧:每周结束之后,花半小时把这周所有做过的题过一遍目录,不用写代码,只看题目名称,然后在心里给每道题标一个“掌握”或“不熟”的标签。这个动作看着简单,却能非常清晰地暴露你的薄弱点,是下周计划的起点。第十五周我就是靠这个目录检查,发现二叉树部分掌握得比想象中好,而动态规划的状态定义还需要继续练,于是把第十六周的主攻方向确定为“状态定义专项训练”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询