:四道经典算法题深度解析)
每年这个时候都有不少同学后台私信我问网易秋招笔试到底考什么、难度怎么样、有没有参考资料。翻了翻硬盘里存的题目正好手头整理过2019年秋招笔试的编程题合集这批题我当年带学弟学妹刷过好几轮里面有不少题目后来在别的公司笔试里也反复出现过属于典型的“校招常青树”。这篇就把合集一里的题目掰开揉碎讲一讲不是简单贴个答案而是把每道题的思考路径、代码实现、复杂度推导、容易踩的坑全部过一遍给正在准备校招的同学一个可以直接照着练的参考。先说这批题的整体感受网易的笔试编程题风格比较稳定不追求偏题怪题但很看重基础数据结构和算法的灵活运用。难度上属于中等偏上不会让你轻松AC但也不会完全无从下手。重点集中在贪心、字符串处理、动态规划、模拟和数学推导这几类题目描述通常不长但每一道都有值得琢磨的细节。这套题适合两类人一类是准备秋招的应届生用来熟悉大厂笔试的出题风格和节奏另一类是刚学完数据结构、想检验自己算法功底的在校生做一遍能明显感觉到自己哪里薄弱。1. 整体布局与解题策略1.1 网易笔试的出题逻辑和考察重点先说个大家最关心的问题网易笔试到底在筛选什么样的人从我刷过的历年真题和周围同事的反馈来看网易的技术笔试核心考察三件事基础扎不扎实、代码能不能一次写对、以及面对陌生问题有没有拆解能力。编程题不会故意考你冷门算法像后缀自动机、单纯形这种基本不会出现但常见的排序、二分、双指针、DP、BFS/DFS一定会覆盖到。这套2019年秋招合集一一共有4道编程题大致分布是1道贪心、1道字符串处理、1道动态规划、1道模拟/数学题。这个分布很典型跟网易当年其他批次的题目结构基本一致。为什么是这个组合因为这几类题最能反映一个候选人的综合能力贪心看思维是否敏锐字符串看编码功底是否细致动态规划看逻辑推导是否严谨模拟题看面对复杂规则时能否保持清醒。另外要提醒一下笔试环境用的是牛客网的系统支持C、Java、Python等主流语言但每道题都有严格的时间和内存限制。我记得当时几道题的时间限制大多是1秒内存限制256MB左右这就意味着你的算法必须控制在O(n log n)甚至O(n)级别O(n²)的暴力解法大概率过不了全部测试用例。做题的时候脑子里要时刻绷着一根弦这个复杂度能不能撑住有没有更优的解法1.2 刷题前的准备和做题顺序建议这套题我建议你当成一次真正的模拟笔试来做而不是看完答案再动手。具体操作是先把4道题都读一遍按自己熟悉的程度排序先做有思路的卡壳超过20分钟就跳过最后再回头啃硬骨头。笔试时间是固定的合理分配时间比死磕一道题划算得多。准备方面语言栈推荐C或JavaPython虽然写起来快但部分题目对执行效率要求比较高用Python需要特别注意常数的优化。我自己当年用的C这套题目的官方题解也大多以C为主所以下面的代码示例统一用C写。如果你习惯用Java或Python理解思路后自己翻译一遍也是一种很好的练习。刷这套题之前建议先熟练掌握这些前置知识点排序算法特别是sort函数的底层原理、双指针技巧、哈希表、基本动态规划的几种模型、BFS/DFS模板。这些都会在这套题里用到。做题的时候准备一个草稿本每个题先画出关键样例的推导过程再写代码。我见过太多同学上来就敲代码最后改来改去浪费时间。笔试考的不只是你会不会还有你在有限时间内能不能稳定输出所以养成先想清楚再动手的习惯特别重要。2. 经典贪心题糖果分配问题2.1 题目描述与输入输出示例这道题我记得很清楚题目大概是这样幼儿园老师手上有若干袋糖果每袋糖果的数量不完全相同现在要把这些糖果分给一群小朋友。每个小朋友有一个“满意值”需求即这个小朋友至少需要拿到多少颗糖果才会开心。老师希望尽可能让更多的小朋友开心问最多能让多少个小朋友开心。输入格式是两行第一行是每袋糖果的数量数组第二行是每个小朋友的满意值需求数组。输出一个整数表示最多能满意的小朋友数量。举个具体例子糖果数量是 [1, 2, 7, 4, 6]小朋友需求是 [3, 1, 5, 8]那最多能满足3个小朋友。一种分法是需求1的小朋友拿1颗那袋需求3的小朋友拿4颗那袋需求5的小朋友拿6颗那袋8颗那袋没有对应的糖果剩余所以最多3个。2.2 贪心策略的推导过程和反例分析这道题的标准解法是贪心策略是先把糖果数量和小朋友需求都从小到大排序然后用两个指针分别遍历。如果当前最小的糖果能满足当前需求最小的小朋友就同时向后移动计数加一如果满足不了说明这颗糖果太小了对任何人都没有用直接丢弃即只移动糖果指针。为什么这个策略是对的核心逻辑是当我们把需求最小的孩子和最小的糖果做匹配时如果最小的糖果都满足不了最小的需求那这颗糖果肯定满足不了任何更大的需求留着它只会浪费空间所以要果断丢掉。反过来如果最小的糖果能满足最小的需求我们就把这颗糖果分给这个小朋友这个选择是最优的因为需求更小的小朋友用更小的糖果就能满足把更大的糖果留给后面需求更大的小朋友整体收益才会更大。这里有个常见的误区有人会觉得应该让每一颗糖果都物尽其用比如用最小的糖果去试最大的需求这样做是错误的。我举个例子糖果 [2, 100]需求 [1, 99]。如果用最小糖果匹配最大需求2 99不满足然后100 1满足了最后只满意1个人。但正确做法是2匹配1100匹配99能满意2个人。所以一定要同序配对方向不能反。2.3 C代码实现与关键细节处理#include bits/stdc.h using namespace std; int main() { vectorint candies, needs; int x; // 读取糖果数组 while (cin x) { candies.push_back(x); if (cin.get() \n) break; } // 读取需求数组 while (cin x) { needs.push_back(x); if (cin.get() \n) break; } sort(candies.begin(), candies.end()); sort(needs.begin(), needs.end()); int i 0, j 0; int satisfied 0; while (i candies.size() j needs.size()) { if (candies[i] needs[j]) { satisfied; i; j; } else { i; } } cout satisfied endl; return 0; }代码说起来很简单但有几个细节要特别注意。第一个是输入读取牛客网的笔试题输入不保证每行固定长度所以用while (cin x)配合cin.get() \n判断行尾是一个稳妥的写法。第二个是排序后指针边界while循环里i和j都要判断越界否则容易访问到end()的位置导致运行错误。第三个是相等情况的处理当candies[i] needs[j]时也是满足条件的代码里用的是这里千万别写成。复杂度方面排序 O(n log n m log m)双指针扫描O(n m)整体能轻松通过1秒的时间限制。内存上只用了一维数组存储输入O(n)级别完全没问题。2.4 容易踩的坑和笔试中的变形考法这个题的时间限制主要是排序和扫描真正容易翻车的是两个地方。第一个是审题不清题里说的是“一袋糖果只能分给一个小朋友”一个小朋友也只能拿一袋不能把两袋糖果拆开凑给一个人。所以实际上就是经典的“饼干分孩子”问题。第二个是输入格式有的同学默认第一行固定是糖果数量第二行固定是小朋友需求但实际上有些测试用例会先给需求再给糖果如果你硬编码顺序就会出错。我在笔试时习惯的做法是读完两行后判断一下谁是谁很难所以只能靠读题仔细看题目描述里先出现的数组是什么然后严格按照那个顺序读取。另外这题还有个变形如果把“一袋糖果只能给一个小朋友”改成“每个小朋友可以拿多袋糖果但总颗数不能低于需求”那解法就变了你需要用前缀和或二分来优化。这种变形考法在后面几年的笔试里确实出现过建议大家理解贪心的本质而不是死记代码。本质是资源分配类的问题先排序再贪心从小到大匹配避免资源浪费。3. 字符串处理题连续字母去重3.1 题目描述与题意理解第二道题是字符串处理题目描述不复杂给定一个只包含小写字母的字符串你需要把字符串中所有连续出现至少3次的相同字母全部删掉删除后两侧的字母会拼接在一起如果拼接后再次出现连续相同字母且次数达到3次或以上则还需要继续删除直到最后不存在任何连续3次及以上相同字母为止。输出最终剩下的字符串。举个例子输入 aaabbbc其中aaa和bbb都连续出现3次删除后变成c输出 c。再比如 aabbbaa先删除bbb变成aaaa此时aaaa连续4次继续删除最后变成空串。这个题看起来简单但“删除后拼接可能引发连锁反应”这个条件就是核心考点。它其实是一个典型的“消消乐”模型跟某些游戏里消除后重新合并的规则很像。3.2 从暴力模拟到栈优化的思路演进拿到这个题很多人的第一反应是写一个循环扫描字符串找到连续3个及以上的相同字符删除从头再扫直到没有可删除的为止。这种方法能过样例但时间复杂度是O(n²)甚至更高因为每删除一次都要重新扫描整个字符串如果字符串长度到10⁵级别肯定超时。怎么优化关键在于发现删除操作的“局部性”。你想啊当某个位置的字符被删除后只有它左右两侧原本不相邻的字符才会拼接在一起才有可能形成新的连续串。中间那些没受影响的字符它们之间的相邻关系根本没有变化不需要重新扫描。这种“只关心局部变化”的问题天然适合用栈来处理。具体思路是用栈保存已经处理过的字符每次读入一个新字符时先判断它是否跟栈顶的字符相同。如果相同就累加当前字符的连续次数如果不同就把前一个字符的连续次数和字符本身记下来然后开启新的一段。但直接这样还不够因为当我们删除一段字符后新字符可能继续与栈顶拼接。更简洁的做法是遍历原始字符串对于每个字符如果当前栈顶字符和它相同就继续累积如果不同就检查栈顶连续次数是否3是就弹栈然后再比较新字符与新的栈顶直到稳定。3.3 双端队列实现和边界情况这里我直接给出一个比较稳的写法用vectorpairchar,int来模拟栈每个元素存字符和它连续出现的次数。遍历一遍字符串每扫描一个字符分两种情况处理如果stk非空且栈顶字符等于当前字符就把栈顶的cnt加一然后如果cnt达到3就弹出栈顶这一步天然处理了连锁反应。如果栈顶字符不等于当前字符就把当前字符作为一个新的段压入栈中次数为1。#include bits/stdc.h using namespace std; int main() { string s; cin s; vectorpairchar,int stk; for (char c : s) { if (!stk.empty() stk.back().first c) { stk.back().second; if (stk.back().second 3) { stk.pop_back(); } } else { stk.push_back({c, 1}); } } string ans; for (auto p : stk) { ans string(p.second, p.first); } cout ans endl; return 0; }这段代码看着短但逻辑很精巧。关键在于每个字符入栈时我们不是等到扫描完一整段再判断而是实时累加计数一旦计数到3立即弹栈。弹栈之后下一个字符进来时直接与新的栈顶比较自然就处理了“拼接后再次出现3个以上”的连锁情况。举个例子输入aaabaaa。扫描a 入栈: [(a,1)]a 计数: [(a,2)]a 计数达到3: 弹栈 - []b 入栈: [(b,1)]a 入栈: [(b,1),(a,1)]a 计数: [(b,1),(a,2)]a 计数达到3: 弹栈 - [(b,1)]结果为 b。正确的。再验证一个容易出错的例子aabbbaa。a, a - (a,2)b - (a,2),(b,1)b - (a,2),(b,2)b - (a,2),(b,3) - 弹b - (a,2)a - (a,3) - 弹a - 空a - (a,1)最终结果 a。不过这里要小心如果删除后新的拼接形成了新的超过3个的连续段我们的代码是能够自动处理的因为弹栈后新字符会继续跟新栈顶比较不需要回溯。这个特性保证了整体只需要一次遍历时间复杂度O(n)空间O(n)。3.4 相似题型的扩展思考这个题跟LeetCode 1209“删除字符串中的所有相邻重复项 II”几乎一样只是那道题要求的是删除连续k次而这道题是连续3次本质相同。做完这道题建议顺手把这类题归类到一个专题下包括括号匹配、标签解析、表达式求值等都属于“栈处理局部变化”的模型。网易笔试中这类字符串题通常不会只考一个简单遍历一定会加一些额外限制来考察你的思考深度比如要求输出删除后的最小字典序、或者要求删除次数最多、或者问最终长度。我见过一个变形删除后要求字典序最小那栈的做法就得额外加一个“贪心”判断在入栈前如果当前字符比栈顶字符小且栈顶字符在后面还会出现就提前弹掉栈顶。这种题就是在栈的基础上叠加了贪心策略建议有精力的同学一并练习。4. 动态规划题跳石板问题4.1 题目描述与实际应用场景第三题是一道非常经典的动态规划题目叫跳石板。题目背景是小易站在编号为N的石板上他需要跳到编号为M的石板上N M。每次跳跃他可以从当前石板x跳到x y其中y必须是x的一个约数不包括1和x本身。也就是说如果当前在石板上编号为x他能跳的距离是x的所有非平凡约数。问从N跳到M最少需要跳几次如果无法到达输出-1。举例N 4M 24。4的约数不包括1和4是2所以从4可以跳到6。6的非平凡约数是2和3所以从6可以跳到8或9。最后怎么跳过去大家可以自己推一下答案是5次4 - 6 - 8 - 12 - 18 - 24。这道题描述的是“最少步数到达目标”的模型本质上是一个最短路径问题但因为每步能跳的距离由当前位置的约数决定所以它天然满足动态规划的无后效性。我在实际教学中常拿这个例子来讲DP的递推过程因为它比背包问题更“有画面感”学生容易理解状态转移是怎么发生的。4.2 状态定义与递推公式我们要算的是从N到M的最小步数直接定义状态dp[i]表示从N跳到i所需的最少步数初始时dp[N] 0其他位置设为无穷大。因为位置只能从前往后跳所以从N到M的方向是单调递增的我们可以从N开始依次遍历每个位置i如果dp[i]已经有值就枚举i的所有非平凡约数y更新dp[i y] min(dp[i y], dp[i] 1)。这个递推过程跟BFS很像但好处是DP数组天然记录了到每个位置的最短距离不需要显式维护队列。注意这里每个位置可能被前面多个位置更新而我们用min来保证取到最小值这就保证了最优子结构。状态转移的核心在于枚举约数。如果你对每个位置i都从2到sqrt(i)循环一遍找约数时间复杂度是O(M * sqrt(M))在M达到10⁵级别时可能勉强能过但如果M到10⁶就有点悬了。所以需要优化约数枚举的方式。4.3 筛法优化与代码实现优化的思路是用筛法预处理每个位置的约数。具体来说我们开一个二维向量divisors对于每个整数i从2*i开始每次加i把i添加到这些倍数的约数列表里。这样预处理完每个位置的约数列表就是完整的而且总复杂度是O(M log M)比逐个求约数快很多。#include bits/stdc.h using namespace std; int main() { int N, M; cin N M; vectorvectorint divs(M 1); for (int i 2; i M; i) { for (int j i * 2; j M; j i) { divs[j].push_back(i); } } const int INF 1e9; vectorint dp(M 1, INF); dp[N] 0; for (int i N; i M; i) { if (dp[i] INF) continue; for (int d : divs[i]) { if (d 1 || d i) continue; // 排除平凡约数 int nxt i d; if (nxt M) { dp[nxt] min(dp[nxt], dp[i] 1); } } } if (dp[M] INF) cout -1 endl; else cout dp[M] endl; return 0; }这里有个容易错的点预处理的时候我们把每个约数i加到了它的倍数j的约数列表中但i本身可能是1或者j本身。对于位置j来说j的约数列表里不应该包含1和j所以处理时要把这两个排除。我习惯在筛选时直接跳过这些也可以在DP时排除效果一样。跑一下N4, M24的例子预处理divs[6] {2,3}divs[8] {2,4}divs[12] {2,3,4,6}等等。dp[4]0。i4d2更新dp[6]1。i6d2,3更新dp[8]2, dp[9]2。i8d2,4更新dp[10]3, dp[12]3其实dp[12]更好的是从6走两步即 dp[6]12所以后来被更新为2。如此继续最终dp[24]5。4.4 这类DP题的复杂度边界与答题策略跳石板这个题如果M的范围给到10⁵筛法预处理O(M log M)是能过的但要注意内存vectorvector 存约数列表每个数字平均约数个数大概在几十左右10⁵级别大概几百万个int内存占用在几十MB还在限制内。但如果M到10⁶约数列表的内存会明显增长需要谨慎。DP的循环是O(M * average_divs)average_divs大概O(log M)所以总体O(M log M)时间上是安全的。这个题在笔试里还有好几个变体比如把“约数不能是1和自身”换成“必须是质数”或者加一步“每次跳跃可以走约数步也可以走质数步”核心都是状态转移只是枚举策略变了。建议大家做完这题后把求约数的几种方式试除法、筛法、质因数分解都熟练掌握很多数论类DP都是这个套路。5. 模拟与数学题圆圈报数问题5.1 题目描述与规律观察第四题是模拟题题干很经典就是约瑟夫环的变种。题目是有n个人围成一圈按顺序编号为1到n从第1个人开始报数报到k的人出圈然后从下一个人开始重新从1报数直到只剩下一个人为止输出最后留下的那个人的编号。输入n和k输出最后留下的编号。比如n5, k2过程是2出圈、4出圈、1出圈、5出圈最后剩下3。如果n、k的范围比较小比如n 1000直接模拟队列或链表就可以但这道题我记得特别之处在于n和k都可能到10⁶级别直接模拟会超时。这就要求你用数学方法来解即约瑟夫环的递推公式。5.2 递推公式的推导与理解约瑟夫环的数学解法非常优雅。定义f(n, k)为n个人、报数到k出圈时最后幸存者的编号用0-based即编号从0到n-1最后再加1还原。递推关系是f(1, k) 0 f(i, k) (f(i-1, k) k) % i这个公式的原理是当i个人时第一轮报到k的人出圈他的编号是 (k-1) % i。剩下i-1个人重新编号从出圈者的下一个人开始原来的第 (k % i) 个人变成了新的第0个人。所以原来第i-1规模的幸存者编号映射回i规模的编号时需要加上k再对i取模。这个映射关系用公式写出来就是上面的递推。很多同学第一次看这个公式会懵我建议自己动手在纸上推一遍n5, k2的例子把0-based编号列出来走一遍流程就能很直观地理解了。这个公式在所有讲约瑟夫环的资料里都有但真正在笔试里能快速想起来并写对的并不多因为这个推导过程确实有点反直觉。#include bits/stdc.h using namespace std; int main() { int n, k; cin n k; int ans 0; // f(1, k) 0 for (int i 2; i n; i) { ans (ans k) % i; } // ans 是 0-based还原成 1-based cout ans 1 endl; return 0; }这个解法的时间复杂度是O(n)空间O(1)就算n到10⁷也能稳定跑出来。不过看到这里你可能有一个疑问如果k很大比如k 10⁹n 10⁷直接取模也没问题因为每一步都取模了计算量依然是O(n)。唯一要小心的是整数溢出ans k可能超过int范围所以用long long来存更安全。5.3 模拟解法在什么情况下可以用虽然数学解法很优雅但笔试时如果题目要求输出整个出圈序列而不是最后一个幸存者那递推公式就帮不上忙了只能老老实实模拟。这种时候如果n比较小5000以内用vector或list模拟都行如果n很大就要用树状数组或线段树来加速“找下一个存活者”的操作复杂度O(n log n)。这个属于进阶内容2019年这套题里没有考到但后来网易云音乐相关团队的技术面试里我遇到过类似题目大家有兴趣可以自己研究一下。回到这套题本身这题主要考的是“能不能从模拟的暴力思路跳出来用数学归纳法推导递推”。在笔试的限时环境里能走到这一步基本就能把这道题拿下了。5.4 模拟与数学的取舍经验谈我在刷题和实际面试中总结出一个经验凡是这种“圈、报数、出圈”的模型只要看到数据范围超过10⁵第一反应就应该是找规律或者数学递推而不是硬写模拟。因为模拟代码虽然逻辑简单但面对大数据量一定超时而超时在小数据测试用例上还看不出来等系统跑大数据才暴露那时候再回头改就来不及了。反过来如果数据范围很小比如n 100直接模拟反而更稳妥因为数学公式一旦推导错调起来比模拟要痛苦得多。所以做题时先看数据范围再决定用哪种思路这叫“用复杂度反推算法”是校招笔试里最重要的实战技巧之一。6. 常见问题与调试记录6.1 输入读取不规范导致的问题刷这套题的时候我遇到的最普遍的问题就是输入读取。牛客网的笔试系统跟LeetCode不一样不是给你封装好的函数而是需要你自己处理标准输入。很多人在LeetCode上刷习惯了写代码时默认输入已经解析好了结果到了牛客网连数据都读不对。跳石板那题输入是N M两个整数用cin N M没问题。但糖果分配那题输入是两行不定长数组很多人以为每行固定长度结果用固定次数的循环去读第几次运行时才发现行数不固定、数组长度不定直接Runtime Error。这类问题最好的预防方法就是统一用while (cin x)读遇到换行符再停这样无论输入怎么变都不会被坑。6.2 边界条件的遗漏与修正这套题里有好几道题都涉及边界条件漏掉一个就可能导致答案错误。糖果分配里数组长度可能是0也可能是1排序和双指针都要特判。连续字母去重里字符串可能一开始就少于3个字符或者删除后变成空串输出空串时需要换行。跳石板里如果N M或者N M分别要输出-1和0很多人在N M时没有处理直接走了循环最后输出一个很大或者-1之类的怪值。我建议每次写完代码后手动跑几个极端用例检查一下空输入、单元素输入、全部相同元素、没有可行解的情况。这套题里最容易漏的就是“没有可行解输出-1”这种情况跳石板那题如果M是质数可能确实跳不到必须在最后判断dp值是否为INF。6.3 用C写题时需要注意的语法细节既然是笔试编译器版本和语言标准一定要提前确认。网易的笔试环境我记得支持C11所以代码里可以放心用auto、vector的初始化列表、pair等特性但有一些编译器可能会默认使用C98的老标准所以最保险的写法是vectorpairchar,int stk;这种风格注意在两个之间加空格避免被误解析成右移运算符。现在的编译器基本都支持C11但我在牛客网上确实遇到过旧标准的坑所以这里提醒一下。另外bits/stdc.h这个万能头文件在牛客网是支持的但在某些本地编译环境里可能不可用。如果平时用VS写代码建议改成标准的#include iostream、#include vector、#include algorithm等。笔试前先在牛客官网的模拟环境里测试几道题确认你的代码风格能不能正常编译运行这一点特别重要我见过有同学在本地运行得好好的提交却编译失败原因是本地编译器版本和线上不一致。6.4 时间超限和内存超限的排查思路如果一道题提交后提示超时不要急着换算法先看看自己的代码是不是有什么常数级别的浪费。比如用了太多层STL容器的拷贝、在循环里反复构造string、或者对同一段数据做了多次不必要的排序。如果确定算法复杂度没问题那大概率是输入输出拖了后腿。C里如果数据量很大用cin/cout一定要加ios::sync_with_stdio(false); cin.tie(0);否则会比scanf/printf慢很多。我在牛客网就吃过这个亏加了之后速度能提升几倍这种优化在笔试里屡试不爽。内存超限的情况相对少但跳石板这种用二维vector存约数的题如果M开到很大确实可能出现内存不够。排查方法很简单计算一下你的数据结构大概占用多少字节比如vectorvectorint在10⁶级别可能就要几百MB肯定超限。这时就要换思路比如不预处理全部约数改用在DP过程中临时分解约数用空间换时间还是时间换空间要根据题目限制灵活取舍。7. 从题目到面试的延伸思考7.1 笔试题目背后的能力考察逻辑这套题做完我建议回过头来想一想网易到底通过这四道题在考察什么专业能力表面上看是算法题实际上是在模拟真实工作中的问题解决流程。糖果分配对应的是资源调度场景连续字母去重对应的是数据清洗和规则处理跳石板对应的是图上的最短路径规划约瑟夫环对应的是复杂逻辑的数学抽象。这些能力在实际的产品研发中都会用到。网易的面试风格一向比较务实笔试考过的算法面试中可能会换个马甲再次出现。比如跳石板这种需要预处理约数的题面试官可能会问你“如何快速求一个数的所有约数”这就从算法题变成了数学基础题。如果你在笔试阶段就把这些前置知识点理解透彻面试时就能对答如流。7.2 刷题的正确姿势和复盘方法最后聊聊刷题的方法。不是把代码码到编辑器里通过了就完事一定要复盘。我的建议是每道题做三遍第一遍独立思考能做出来最好做不出来就看题解看懂后关掉题解自己重写一遍第二遍隔一天再做检验自己是否真正掌握了思路而不是背代码第三遍在面试前快速重刷只需要看题目描述在脑子里过一遍解法遇到卡壳再回头翻笔记。复盘的时候不仅要把正确解法记录下来还要记录自己第一次做错的原因。是自己的思维盲区、手误、还是对某个语言特性不熟悉把这些记录到错题本里每周翻一遍。我当时带学弟学妹刷这套题的时候发现他们最大的问题就是做题量上去了但错误模式没有收敛同样的边界错误在不同题目里反复出现。这其实不是算法能力的问题而是复盘做得不够系统。7.3 后续还可以从哪个方向继续深入这套“合集一”只是2019年秋招编程题的一部分后面还有合集二、三覆盖了更多题型比如区间DP、状态压缩、滑动窗口、并查集等。我个人建议做完这套题之后如果你时间充裕可以按主题刷题而不是按公司刷题。比如这周专攻“双指针和滑动窗口”把LeetCode上相关的中等难度题刷30道下周专攻“动态规划的状态定义”从线性DP到区间DP再到树形DP逐步推进。等这些专题都过了一遍后再回来整套刷真题你会发现自己做题的速度和准确率都有了质的提升。这也是我个人从校招到现在带新人一直在用的方法效果比较稳定希望能对正在准备笔试的同学有帮助。