资讯动态

LeetCode刷题97天:二分、动规、回溯经典题复盘与高效刷题法

发布时间:2026/10/20 8:14:55 来源:尧图企业网站定制
第97天了。如果把这个数字放在日历上正好是三个多月从最初刷题五分钟就得翻一次题解到现在拿到一道中等题能先想清楚数据结构再动手这个过程回头看看其实挺感慨。今天练的三道题是LeetCode面试经典150里的《搜索旋转排序数组》《最长回文子串》《组合总和》一道二分、一道动规、一道回溯恰好覆盖了面试里最高频的三类题型。这篇就把今天的完整思路、踩过的坑、以及这97天总结出来的一套刷题方法都写下来给正在啃题单的朋友做个参考。1. day97 当天的题目复盘三道题的完整解体过程1.1 搜索旋转排序数组二分查找的变形思路先说这道二分变体题。数组原本是升序的在某个未知位置旋转了一次比如 [0,1,2,4,5,6,7] 变成 [4,5,6,7,0,1,2]现在要在这个数组里查找一个目标值要求时间复杂度是 O(log n)。第一眼看上去数组不是整体有序的二分好像用不了。但仔细看会发现一个关键性质虽然整个数组不再有序但任意取一个中点mid 的左右两侧至少有一侧是严格有序的。这就是原题能解的核心突破口。我今天的实现思路是这样的def search(nums, target): left, right 0, len(nums) - 1 while left right: mid (left right) // 2 if nums[mid] target: return mid # 左半部分是有序的 if nums[left] nums[mid]: if nums[left] target nums[mid]: right mid - 1 else: left mid 1 # 右半部分是有序的 else: if nums[mid] target nums[right]: left mid 1 else: right mid - 1 return -1判断的关键在于nums[left] nums[mid]这个条件。如果成立说明左半段没有发生旋转断裂是一个完整的升序区间那么 target 落在[nums[left], nums[mid])范围内就向左收缩否则向右。如果左半段不满足有序那断裂点一定在左边右半段必然有序就反过来处理。注意这个等号的归属。nums[left] nums[mid]必须带等号否则当 left 和 mid 指向同一个元素时会漏掉边界情况。实测下来这个等号是这道题最容易被忽视的细节。还有一个小坑是区间边界的开闭。我倾向于在判断 target 范围时左侧用右侧用配合nums[mid]已经被排除相等的情况这样循环退出条件和区间收缩都不会出错。如果两侧都用闭区间很容易出现 left 和 right 交错但还没有退出循环的怪事。这道题做完之后我顺手把带重复元素的变体也想了一下就是先收缩左右边界再去二分但那个解法的时间复杂度最坏会退化到 O(n)面试时如果被追问能说出来这一步就已经足够了。1.2 最长回文子串动态规划与中心扩展的双解法第二题是找字符串中的最长回文子串。这道题在面试里出现频率极高两种主流解法我都各写了一遍收获不少。先给动态规划的版本。定义dp[i][j]表示s[i:j1]是否为回文串状态转移的核心逻辑是一个子串是回文当且仅当两端字符相同且去掉两端后的中间部分也是回文。def longestPalindrome(s): n len(s) if n 2: return s dp [[False] * n for _ in range(n)] start, max_len 0, 1 # 所有长度为1的子串都是回文 for i in range(n): dp[i][i] True # 按子串长度从小到大递推 for length in range(2, n 1): for i in range(n - length 1): j i length - 1 if s[i] ! s[j]: dp[i][j] False else: if length 2: dp[i][j] True else: dp[i][j] dp[i 1][j - 1] if dp[i][j] and length max_len: start, max_len i, length return s[start:start max_len]这个写法里最关键的点是遍历顺序。dp[i][j]依赖dp[i1][j-1]也就是长度更短的子串所以必须按子串长度从小到大计算不能按 i 从上到下。我第一次刷这题时就是在这里栽的跟头用双重 i/j 循环去填表结果短的还没算出来就去算长的一堆边界直接崩掉。空间复杂度 O(n^2)。如果想优化可以用中心扩展法把空间压到 O(1)。中心扩展的思路非常朴素回文串一定有一个中心这个中心可能是单个字符奇数长度回文也可能是两个相邻字符偶数长度回文。遍历所有可能的中心向两侧扩展记录最长的那个。维度动态规划中心扩展时间复杂度O(n^2)O(n^2)空间复杂度O(n^2)O(1)实现难度状态转移较绕边界处理直白额外价值可继续求解所有回文子串数量代码短面试手写快面试时我建议先说动态规划思路显得有条理然后主动提出还能用中心扩展把空间降下来再写 O(n) 空间版本。这些对比本身就是加分项比闷头写一种解法要好得多。1.3 组合总和回溯排序剪枝的经典模板第三题是组合总和。给定一个无重复元素的数组 candidates 和一个目标数 target找出所有可以使数字和等于 target 的组合。同一个数字可以无限制重复使用。这题是回溯法的标准模板题核心套路就四个字选择、递归、撤销。我今天的写法是在回溯前先排序排序的好处是可以在递归过程中及时剪枝——如果当前数字已经大于剩余目标值那后面的数字只会更大直接放弃这一分支。def combinationSum(candidates, target): res [] path [] candidates.sort() def backtrack(start, remaining): if remaining 0: res.append(path[:]) return for i in range(start, len(candidates)): if candidates[i] remaining: break # 剪枝排序后后面的数更大 path.append(candidates[i]) backtrack(i, remaining - candidates[i]) # 可重复使用所以传 i path.pop() # 撤销选择 backtrack(0, target) return res这个start参数是整个去重逻辑的灵魂。它保证了一个组合里数字的选取顺序是非递减的比如先选了 2 再选 3之后就不会再出现先选 3 再选 2这个组合所以像 [2,3,3] 和 [3,2,3] 这种重复组合就不会被同时算进结果。如果面试题改成每个数字只能用一次只需要把递归里的backtrack(i, ...)改成backtrack(i 1, ...)然后额外加一个同层去重判断就是另一道经典题《组合总和 II》的解法了。回溯的复杂度分析一直是个让人头疼的地方。这道题在最坏情况下每个元素都有选或不选的无限扩展分支时间复杂度可以视为 O(S)其中 S 是所有可行解的长度之和空间复杂度取决于递归深度即 target 除以最小候选数。面试时把这个话术准备好基本不会被问住。2. 面试经典150题的整体拆解与路线规划2.1 为什么选择这条题单它解决了什么问题LeetCode 上的题单有很多但面试经典150题这个列表是我比较推荐的一条主线。它不像单纯按难度刷那样容易陷入简单题不会做、难题更不会做的死胡同也不像某些几千题的题海战术那样刷到后面自己都忘了前面学过什么。这个题单的特点是按专题组织、覆盖面试高频题型链表、二叉树、字符串、动态规划、滑动窗口、二分、回溯、图论、位运算都有涉及且每个专题的题目数量控制得比较合理。我97天走下来最大的感受是刷题这件事最怕的不是题难而是没有体系。今天刷一道链表明天刷一道动归大脑很难形成有效的知识结构。面试考的是拿到问题能快速定位到对应解法这本质上是在检索自己的知识索引。150题的价值就在于给了一套相对完整的索引每个专题刷过几道之后再看到同类题大脑会自动关联这是二分答案还是双指针这是区间 DP 还是状压另一个很实际的好处是时间可控。按一天2-3道新题加复习的节奏差不多3-4个月能过完全部的题。这个周期和大多数人准备面试的时间窗口吻合既能保证覆盖度又不至于战线拉得过长导致倦怠。2.2 按专题拆解150题的时间分配策略150题不是一个一个按顺序硬啃的。我根据自己的实践把专题分成了几个优先级梯队不同梯队投入的时间完全不同。第一个梯队是数组/字符串、链表、二叉树这三个专题占了面试题量的半壁江山而且考察的知识点相对独立适合最先攻破。链表题的核心就是指针操作和 dummy 节点技巧二叉树的核心是递归遍历的框架思维。我当时花了两周集中过这三个专题每天三到四道优先级最高。第二个梯队是双指针、滑动窗口、哈希表、栈与队列这些是数组/字符串题目的延伸也是中等题的高产区域。滑动窗口这一类题目其实有很强的套路维护窗口的左右边界、用哈希表记录窗口内的状态、在窗口移动过程中更新答案。把这个套路弄清楚之后连续五六道同类题都能直接套模板。第三个梯队是动态规划、回溯、二分查找、贪心这些是思维含量最高的部分理解成本比前两个梯队高不少。我建议把它们安排在刷题中期大脑已经适应了算法思维之后再去啃。特别是动态规划从最长回文子串到编辑距离再到打家劫舍系列题型变化极大但底层能力就两个定义状态和推导转移方程。第四个梯队是图论、字典树、位运算、堆、区间等冷门或进阶专题。这些不用花太多时间每个专题过几道经典题、熟练掌握模板即可面试实际考到冷门专题的概率不高但万一考到了也不能完全没思路。我自己的时间分配大致是前4周搞定第一梯队第5-8周搞定第二梯队第9-14周攻克第三梯队最后两周专门做冷门专题加整体复习。用表格看更直观时间阶段覆盖专题每日题量核心目标第1-4周数组/字符串、链表、二叉树3-4题建立基本思维框架第5-8周双指针、滑动窗口、哈希表、栈队列2-3题熟悉中等题套路第9-14周动态规划、回溯、二分、贪心2-3题突破思维瓶颈第15-16周图论、位运算、堆、区间1-2题查漏补缺专项复习3. 刷题方法论怎么练才能让一道题有十道题的效果3.1 做题前的三分钟思考框架很多人刷题有个坏习惯读一遍题目发现没思路下意识就打开题解。我早期也这样直到某一天发现自己一天刷了10题但第2天全忘了才意识到这种刷题只是自我感动。后来我给自己定了一条规矩看到题目后无论有没有思路都必须先做三分钟主动思考思考的框架固定为三步。第一步分析数据范围。这道题数组长度是10^5还是10^3如果是 10^5那 O(n^2) 解法大概率超时目标就是 O(n log n) 或 O(n)如果是 10^3那 O(n^2) 的 DP 完全可以接受。数据范围其实是题目给的第一个提示很多人忽略这一点白白浪费时间。第二步想一下暴力怎么做。暴力解不一定写得出来但必须想清楚暴力为什么慢、慢在哪一步。比如最长回文子串的暴力解法就是枚举所有子串再判断是否回文慢在每次判断都要 O(n)。知道慢在判断这一步自然就想到了用 DP 空间换时间或者中心扩展直接省掉重复判断。第三步回忆这个暴力过程的瓶颈能怎么优化。是在枚举时剪枝是引入哈希表把查找变成 O(1)还是用双指针把嵌套循环降维这三分钟的思考不要求一定得出正确答案但会强迫大脑把新题和已有知识库做一次连接。今天这三道题我思考过程里都在心里复述了一遍对应的模板二分变体怎么判断有序区间、DP 的遍历顺序怎么定、回溯怎么传 start 参数去重。这种读到题→唤醒模板→套用并调整的链路才是刷题真正在训练的底层能力。3.2 复盘与笔记把错题转化成面试素材如果把刷题比作学习做菜那做题只是把菜做出来而复盘才是真正品尝和吸收的过程。我见过太多人追求 AC 数量AC 完一道题立刻滑到下一道连自己用的是哪种思路、题解区有没有更优解都不看这是非常可惜的。我现在的复盘流程分四步先把我的代码和最优解对比一遍看我写的是不是真的最优解法然后是看题解区重点不是看代码而是看评论区的高赞思路解读同一道题多几个视角理解会完全不同接着是总结我怎么没想到这个解法也就是找自己思维路径和最优解法之间的差距最后是把这道题的模板和易错点记到笔记里。今天的三道题我的笔记分别是这么记的搜索旋转排序数组二分变体判有序区间时注意等号归属模板是判断哪边有序→判断 target 在哪边→收缩区间最长回文子串DP 版本注意按长度遍历中心扩展版本注意奇偶两个中心来源组合总和回溯模板排序剪枝start 去重改 i 或 i1 可以适配可重复使用/不可重复使用两种变体笔记不长每道题两三行但都是直击要害的一句话经验。这些笔记在我后面复习时发挥了极大作用第97天回头看以前的笔记很多当时觉得很绕的内容现在一眼就能读懂。这说明笔记不是抄题解而是记录理解进化的过程。4. 长线刷题的心态管理与疲劳期应对4.1 第97天最容易出现的状态问题说实话刷到第97天这个节骨眼心态上很容易出现一些问题。最典型的是麻木感打开题目界面看着题目描述大脑一片空白既没有兴奋也没有紧张纯粹机械化地想把题解完。这种状态很危险因为刷题一旦变成机械的重复效率会急剧下降今天做完的题明天就没有任何印象。另一种是焦虑感刷了97天的题感觉还是有不会做的题甚至有时候一道简单题都要想很久就开始怀疑自己到底有没有进步。这种焦虑我也有过后来想明白了一件事——刷题是一个复利积累的过程前面几十天的积累不会马上在正确率上体现但会在某个临界点之后突然爆发就像背单词背到第N天突然能看懂外刊一样。关键是不要在临界点到来之前放弃。应对麻木和焦虑我的方法其实很简单允许自己适当降低目标。状态好的时候一天做四道题状态差的时候一道题搞明白一个思路都行。长期坚持的核心不是每天高强度输出而是每天都保持和算法的接触哪怕只是写10分钟代码也比彻底断档要好。我今天给自己定的目标就是三道题加一篇复盘笔记做完之后还有余力又去翻了翻之前错题的笔记这种节奏很舒服。4.2 我的定期复习节奏与自测手段只刷新题不复习等于只吃饭不消化。我之前提到过复习的重要性这里展开说下我的具体节奏。我的复习频率分三层当天错题当晚复习把正确解法自己不看笔记重新写一遍每周日把这一周做错的题统一过一次重点看错误原因是否重复出现每两周随机抽5道已经 AC 过的老题重新做一遍用来检验是记住了解法还是真正理解了思路。最后这种随机抽老题的方法我觉得特别有效。因为按顺序复习会产生记忆惯性一道题刚做完一周答案是A还是B很容易想起来。随机抽就没有这个问题你得真真切切重新分析一遍题目。我第97天晚上就随机抽了一道第30天左右做过的树相关题目重新做的时候发现当时的解法很笨没有用递归的返回值来传递信息而是搞了个全局变量——这说明这段时间的刷题确实带来了思维上的提升这种实打实的正反馈比任何鸡汤管用。还有一个自测手段是限时模拟。找一道没做过的中等题给自己20分钟不开编辑器的自动补全模拟真正面试时的环境。面试时能在白板上写出来的代码才是有效的代码这种模拟练的不仅是解题能力还有时间分配和临场抗压能力。我大概从第60天开始每两天做一次这样的限时模拟今天这三道题里有两道就是从模拟题单里挑的。5. 面试前一周最值得做的几件事5.1 高频题型的快速检索表如果你已经刷完了150题的大部分但面试就在一周后这时候最忌讳的就是闷头刷新题。新题是刷不完的而且短期记忆容量有限刷一道忘一道反而打乱已有体系。这个阶段的正确策略是回到自己最熟悉的知识模块把高频题型的模板做一次快速检索。我自己整理了一个速查表面试前一天晚上会快速过一遍这里直接分享出来题型核心模板关键注意点数组二分while left right每次排除一半区间等号归属、边界收缩方向链表操作dummy 节点 双指针断链顺序画图辅助滑动窗口右扩左缩哈希表维护状态窗口合法条件二叉树遍历递归三要素返回值定义要明确回溯/组合排序 start 去重 撤销同层去重 vs 树枝去重动态规划状态定义 转移方程 遍历顺序初始化边界压缩维度最短路径BFS / Dijkstra 模板图是否为有权图并查集find 路径压缩 union 按秩合并初始化集合数量这张表不用背重要的是每看到一行都能在脑子里自动浮现出对应的代码框架。如果看到某一行的模板已经开始模糊了就立刻去翻对应的经典题重写一遍这是考前最高效的补漏方式。5.2 白板代码与表达能力练习面试和笔试最大的区别在于面试官看不到你写的代码只能看到你边写边讲的过程。这意味着算法能力只是一半的考核另一半是表达自己思路的能力。很多人平时刷题能 AC一到面试就语无伦次主要原因是从来没练过边说边写这种双线程操作。我从第80天开始在每天限时模拟的基础上加了一步写完代码后用一分钟的时间向空气讲解这道题的思路。讲解的内容固定为三个部分先说暴力解法是什么、慢在哪里再说我选择的解法为什么能解决这个瓶颈最后说代码里的关键边界条件。这三句话说清楚面试官基本就能跟上你的节奏。练习的时候有一个很实用的技巧永远不要先动手写代码先开口说思路。哪怕面试官说你可以直接写也建议花30秒简单说一句思路这会让双方都对代码结构心里有底。我见过有些候选人上来就写写到一半突然发现自己思路有漏洞又得擦掉重来观感很差。开口说思路的过程本质上是一次自我检查思路在说出来的过程中有逻辑断裂其实是能感觉到的。白板代码还有一个要注意的细节写代码的时候不要使用只有在自动补全环境下才能写出来的写法。比如 Python 的类型注解可以写但不要依赖 IDE 给的方法名提示变量名要起得清楚不要在纸上写a、b、x这种一眼看不懂的名字。面试官看你的代码就像在看一个文档可读性和正确性同样重要。最后再分享一个我个人的小体会刷到第97天算法能力当然有长进但更大的变化是心态。刚开始刷题的时候看到一道题没思路就慌觉得是自己太菜现在我反而觉得没思路才是正常的因为思路本身就是靠一次次没思路→看题解→理解→复现→变成本能这样一个循环练出来的。面试的时候遇到没做过的题完全不用害怕因为你平时练的就是处理新题的过程而不是背题的过程。保持这个心态比多刷一百道题可能都有用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑