2020年秋招那阵子,我在牛客网上刷到猿辅导的算法岗笔试邀请,系统提示“猿辅导2020校招笔试(算法岗一)”,一共三道编程题加若干选择题,限时两小时。说实话,看到这个配置我反而松了口气——没有上来就让人写一个大项目,而是老老实实考算法基本功。这场笔试给我留下的印象是:题型设计比较典型,基本覆盖了算法岗笔试最常考的那几类问题——贪心、字符串匹配、图论最短路、动态规划,外加一些机器学习基础。回头复盘,这套卷子虽然不算难,但陷阱不少,值得好好拆一拆。
这篇文章不打算只贴答案,我想从“为什么这么考”“考场上容易踩什么坑”“下次遇到同类题怎么秒反应”三个角度,把这份笔试题完整复盘一遍。无论你是在准备秋招、春招,还是单纯想检验一下自己的算法功底,这篇复盘应该都能派上用场。
1. 笔试概览与题型分布:算法岗笔试到底考什么
1.1 整体结构与时间分配
我当时参加的这场考试,整体结构是“单选题 + 多选题 + 编程题”,但编程题占大头,总分大概在100分。选择题主要围绕数据结构、操作系统、网络,但算法岗的卷子里穿插了相当比例的机器学习内容,比如KNN、K-Means、贝叶斯这些概念题。编程题三道,限时总共两小时。
这里要给所有准备笔试的同学一个建议:拿到试卷先花三分钟通读所有题目,确认每道题的分值和难度,然后决定答题顺序。不要一上来就死磕第一题。我自己的经验是“先易后难,先拿能拿的分”,如果一道题看了两分钟还没有思路,先跳过,把后面的基础题做完再回来看。
另外要注意的是,选择题里往往会埋一些“看似多选题、实际单选”的题目,或者反过来,题干里不写“多选”但选项明显不止一个正确答案。这种题在牛客网等在线笔试系统上很常见,要仔细看清楚题目要求,别凭直觉答题。
1.2 题目难度阶梯与得分策略
从这场笔试来看,猿辅导的题目设计其实有很明显的难度阶梯——不难,但层层递进。第一道编程题是常规的贪心排序,属于送分题;第二道是字符串匹配,考察KMP算法的理解;第三道是图论最短路,需要堆优化Dijkstra或者DP+拓扑的思路。整体来说,如果基本功扎实,两小时内做完是来得及的。
这里要强调一下“AC率”这个概念。笔试系统里能看到自己的提交状态,很多人会反复提交一个超时的代码,白白浪费大量时间。正确的做法是:先把暴力解法写出来确保正确,如果数据规模允许就提交,如果明显会超时,再去优化。我见过太多同学在TLE之后反复调试同一个思路,而不愿意换个角度想,最后时间全耗在上面。
| 考察方向 | 典型题目 | 核心考点 | 时间复杂度要求 |
|---|---|---|---|
| 贪心 | 任务调度 | 排序关键字选择 | O(nlogn) |
| 字符串 | KMP求next数组 | next数组定义与求法 | O(n) |
| 图论 | 单源最短路径 | 堆优化Dijkstra | O((n+m)logn) |
| 动态规划 | 最长上升子序列 | 状态设计与优化 | O(nlogn) |
这道题的难度分布其实在提醒所有求职者:算法岗笔试不是竞赛,不需要你掌握什么高深莫测的冷门算法,而是希望你在限时压力下,能快速识别题目背后的模型,并写出足够高效的解法。如果连基础的数据结构和经典算法都没吃透,那基本告别算法岗笔试了。
2. 编程题复盘:四类高频题型的思路与实现
2.1 贪心:任务调度的排序关键字陷阱
笔试里这道题大概是这样:有n个任务,每个任务有一个开始时间和结束时间,同一时刻只能做一个任务,问最多能完成多少个任务。
这个问题的经典解法已经烂大街了——按结束时间从小到大排序,然后依次选择结束时间最早且不冲突的任务。但笔试里真正刷人的地方在于:为什么不是按开始时间排序?为什么不是按时长排序?
大多数人靠背答案能写对,但一旦题目变形就垮。比如把问题改成“最少需要多少台设备才能完成所有任务”,答案就变成“按开始时间排序+最小堆维护当前设备的最早空闲时间”,和第一问完全相反。如果第一问你按开始时间排序,第二问你按结束时间排序,那两题都得挂。这类变形题在笔试里特别常见,考的就是你有没有真正理解贪心的取舍逻辑。
我贴一段最基础的任务调度代码,大家重点看排序关键字的处理:
def max_tasks(tasks): # tasks: [(start, end), ...] tasks.sort(key=lambda x: x[1]) # 按结束时间升序 count = 0 current_end = float("-inf") for start, end in tasks: if start >= current_end: count += 1 current_end = end return count当时我在考场上做这道题大概只花了5分钟,但旁边的同学跟我交流时说他卡了很久——因为他第一反应是按开始时间排,然后局部最优和全局最优对不上,越改越乱。这就是典型的“知道贪心,但不知道贪心贪的是哪个维度的值”。
2.2 字符串:KMP的next数组是“背了也会错”的考点
第二道题印象很深,题目直接给了模式串p="abacaba",让计算next数组。这里就有一个非常大的坑:next数组在不同教材里有两种定义,一种是“最长相等前后缀长度”,一种是“失配时跳转位置”。如果不先看清楚题目给的公式定义,直接套自己背过的模板,结果一定会错。
我用最长相等前后缀这一定义来算一遍。所谓next[i],表示模式串前i个字符(下标从0开始)组成的前缀子串中,最长的相等前后缀长度。因为前缀和后缀不能是子串本身,所以next[0]通常规定为-1或0,看题目。
p="abacaba",逐位计算:
- next[0] = -1(约定值,或有的题设为0)
- next[1],前缀子串"a",最长相等前后缀长度为0
- next[2],前缀子串"ab",无相等前后缀,0
- next[3],前缀子串"aba",最长相等前后缀是"a",长度为1
- next[4],前缀子串"abac",0
- next[5],前缀子串"abaca",最长相等前后缀是"a",1
- next[6],前缀子串"abacab",最长相等前后缀是"ab",2
- next[7],前缀子串"abacaba",最长相等前后缀是"aba",3
如果题目里定义的next[i]是“当第i位失配时,模式串应该跳转到的位置”,那通常在经典next数组的基础上整体右移一位,并且把next[0]设为-1。KMP的匹配过程就依赖这个跳转数组。笔试时一定要仔细看题面给出的定义,不要想当然。
def build_next(p): m = len(p) nxt = [0] * m j = 0 for i in range(1, m): while j > 0 and p[i] != p[j]: j = nxt[j - 1] if p[i] == p[j]: j += 1 nxt[i] = j return nxt p = "abacaba" print(build_next(p)) # 输出: [0, 0, 1, 0, 1, 2, 3]这段代码输出的是以“最长相等前后缀长度”为语义的next数组(next[0]=0),如果题目用的是“失配跳转位置”的语义,需要在前面补一个-1再整体右移。很多人在这一步踩坑,就是因为没有分清这两种定义。
2.3 图论:堆优化Dijkstra的写法与适用边界
第三道编程题是典型的单源最短路径问题:给定n个节点、m条边,求从起点到终点的最短距离。n和m的数量级大概是10^5,所以Floyd肯定不行,朴素Dijkstra的O(n^2)也危险,必须上堆优化Dijkstra,复杂度O((n+m)logn)。
堆优化的核心思路是:用优先队列维护“当前已知最短距离最小的未确定节点”,每次从堆顶取出一个节点,如果它已经被松弛过就跳过,否则用它去更新相邻节点的距离。这里有一个很容易写错的细节:优先队列默认是大顶堆,存距离时要存负值或者用自定义比较器,否则取出来的就是“最远”而不是“最近”的节点,整个算法直接废掉。
import heapq def dijkstra(n, edges, start): graph = [[] for _ in range(n)] for u, v, w in edges: graph[u].append((v, w)) dist = [float("inf")] * n dist[start] = 0 pq = [(0, start)] while pq: d, u = heapq.heappop(pq) if d > dist[u]: continue for v, w in graph[u]: nd = d + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (nd, v)) return dist这里要注意,Dijkstra只适用于边权非负的图。如果题目出现负权边,哪怕只有一条,Dijkstra也会给出错误结果,应该改用Bellman-Ford或SPFA。笔试里经常故意在题面里埋“边权可能为负数”这个条件,来筛选那些只会背模板的人。
另外,如果你判断出图满足“有向无环”这个条件,那也可以用拓扑排序+动态规划做最短路:按拓扑序处理每个节点,用当前节点的dist去松弛所有出边。这个做法在DAG上比Dijkstra更简洁,也更容易证明正确性。笔试中多掌握一种解法,就多一层保险。
2.4 动态规划:为什么LIS的O(nlogn)解法值得掌握
动态规划题在算法岗笔试里几乎是必考的。这道题考的是最长上升子序列(LIS)。最经典的解法是O(n^2)的DP,状态转移方程是dp[i] = max(dp[j] + 1),其中j < i且nums[j] < nums[i];但一旦n达到10^5,O(n^2)直接TLE,必须用“贪心+二分”优化到O(nlogn)。
优化的核心思路是维护一个数组tails,tails[len]表示长度为len的上升子序列中,最小的末尾元素值。这个数组是单调递增的,所以可以二分查找当前元素应该替换的位置。整个过程本质上是“贪心地把每个元素放到它该放的位置”,理解这一点比背代码重要得多。
def length_of_lis(nums): import bisect tails = [] for x in nums: pos = bisect.bisect_left(tails, x) if pos == len(tails): tails.append(x) else: tails[pos] = x return len(tails)笔试里LIS的变形非常多,比如“最长不下降子序列”“最长公共子序列”“最少拦截系统”等,本质上都是同一个模型加上一点改动。能在考场上快速识别出“这是LIS模型”,远比会背一种解法更有价值。比如“最少拦截系统”那道经典题,问的是按顺序拦截导弹,最少需要几套系统,本质上就是求最长不下降子序列的长度。熟悉模型,你就能在考场上省下大量推公式的时间。
3. 高频考点补全:数据结构基础决定笔试下限
3.1 排序算法:从冒泡到堆排序的理解层次
热搜词里“冒泡排序算法c++”“堆排序算法”“快速幂算法c++”这些词说明一个事实:算法岗笔试对排序算法的考察,往往不是让你手写快排,而是考你“对排序算法的理解层次”。比如选择题经常问:堆排序建堆的时间复杂度是多少?快排的最坏情况发生在什么输入下?归并排序的额外空间复杂度是多少?
这些问题的答案光靠背是不够的。堆排序建堆是O(n),而不是很多人以为的O(nlogn);快排最坏情况出现在基准选择极度不均衡时,比如对已经有序的数组每次都选第一个元素作为基准,退化成O(n^2);归并排序的额外空间是O(n),这些细节都是高频考点。
我建议准备笔试时,把每种排序算法整理成一张表,写清楚:时间复杂度(最好、平均、最坏)、空间复杂度、是否稳定、适用场景。比如数据规模小且基本有序时,插入排序可能比快排还快;需要稳定排序时,归并排序是首选;需要原地排序时,堆排序和快排更合适。这些判断在实际工作中选型也很有用。
3.2 树与图:遍历、拓扑排序与并查集
树和图的题目在笔试选择题里几乎是必出的。树的遍历(前序、中序、后序、层序)是基本功;图的部分,拓扑排序、并查集、二分图判定(染色法)都是热门考点。特别是“拓扑排序”和“有向无环图”的关系,很多选择题喜欢在“有环图无法拓扑排序”这个结论上做文章。
并查集则是“团伙问题”“连通分量问题”等场景的标准解法。有的同学觉得并查集不重要,但事实上,笔试里“判断两个节点是否连通”“求连通分量个数”这类题目,用并查集写起来比DFS/BFS更简洁,也不会超时。
如果让我给一个优先级判断,我会说:树的三种遍历必须闭着眼睛能写;图论里面,拓扑排序、并查集、最短路这三板斧,是你必须掌握的底线。贪心算法和二分图匹配这些高级内容,笔试里出现的频率相对低一些,但一旦出现,会的人就能拉开差距。
3.3 查找与二分:边界条件是最容易翻车的地方
二分查找是笔试里“看起来简单、写起来全错”的重灾区。多少人写二分查找都遇到过死循环或答案差1的问题?原因几乎都出在边界条件的处理上:while循环用left < right还是left <= right?更新区间时mid要不要加1或减1?
一个我个人的经验是:每次写二分前,先在注释里写清楚“我要找的是第一个满足条件的值,还是最后一个满足条件的值”,然后根据这个语义去定边界。语义明确了,代码就不会错乱了。这个经验帮我避免了很多次“看似AC、实际WA”的尴尬。
下面这个模板我建议直接背熟:
# 找第一个 >= target 的位置 def lower_bound(nums, target): left, right = 0, len(nums) # [left, right) while left < right: mid = (left + right) // 2 if nums[mid] < target: left = mid + 1 else: right = mid return left这里用的是左闭右开区间,好处是退出循环时left一定等于right,不用纠结该返回哪一个。如果题目要求的是“最后一个 <= target 的位置”,把判断条件反过来写,返回left - 1即可。记住这一个模板,比死记多个版本的代码可靠得多。
4. 算法岗特有的机器学习与模型考察
4.1 KNN:k值选择、距离度量与特征归一化
算法岗笔试和开发岗最大的区别,就在机器学习这部分。KNN是最常考的基础算法之一,原因不是它有多难,而是它最能考出“你有没有真正理解机器学习的基本流程”。比如选择题会问:KNN中的k值过大会导致什么?答案是模型过于平滑、决策边界粗糙,容易欠拟合。
KNN还有两个非常容易被忽略的细节:一是距离度量,欧氏距离、曼哈顿距离、余弦相似度的适用场景不同;二是特征必须归一化,否则量纲差异会直接扭曲距离计算。笔试里常有一道选择题,给出两个特征(比如年龄和收入),问为什么不直接跑KNN,答案就是“特征量纲差异过大,需要归一化”。
如果你在笔试或者面试中遇到KNN的题目,建议多往“懒惰学习”“非参数方法”“基于实例的学习”这些概念上靠,这些词能侧面体现你的知识广度。KNN本身没有显式训练过程,所有计算都发生在预测时,这也导致了它在样本量大时预测速度非常慢。
4.2 K-Means与聚类:K值怎么选、为什么怕初始中心
聚类算法里,K-Means的出镜率极高。笔试常考的点有:K-Means的迭代流程、K值的选择方法(肘部法则)、K-Means对初始中心敏感、K-Means不适合处理非凸簇。选择题还会把K-Means和KNN放在一起考,让大家区分:K-Means是无监督学习,KNN是有监督学习;一个是聚类,一个是分类。这两个名字太像了,不理解的考生很容易混。
K-Means的迭代流程大致是:随机选K个初始中心,然后把每个样本分配到距离最近的中心,接着根据分配结果重新计算中心,反复迭代直到中心不再变化。这中间有两个细节容易被问:一是初始中心的选择对结果影响很大,所以实际工程中经常用K-Means++来初始化,或者跑多次取最优;二是聚类的评价指标,比如轮廓系数,是笔试和面试都可能延伸考的点。
4.3 BM25与信息检索算法:搜索场景的入门考点
因为猿辅导是教育科技公司,招聘算法岗时偶尔也会涉及搜索、推荐方向的基础。BM25这个算法在热搜词里出现了,说明很多算法岗位确实会考察信息检索相关的知识。BM25的核心思想是:一个词在文档中出现的频率越高、文档越短、且这个词在整个文档集合中越稀有,这个词对当前文档的评分贡献就越大。理解这个思路比背公式重要,因为笔试很少让你手推BM25公式,但会给你一个场景,让你判断“文档相关性排序”。
当年我准备这块时,是把BM25、TF-IDF、向量空间模型放一起对比着记的。TF-IDF是BM25的简化版,BM25引入了文档长度归一化和词频饱和效应,实际效果通常更好。如果你时间有限,优先把BM25和TF-IDF的区别搞清楚,就足够应付大多数笔试了。
4.4 智能优化算法:粒子群、模拟退火的概念级考察
智能优化算法(粒子群、模拟退火、遗传算法)在笔试里通常以概念题出现,比如问“模拟退火的温度T的作用是什么”“粒子群算法的速度和位置更新公式中,惯性权重的作用是什么”。这类题不需要你能从头实现,但要知道它们解决什么问题、基本流程是什么、关键参数有什么含义。如果时间有限,优先掌握模拟退火和粒子群的概念即可。
模拟退火的核心是“以一定概率接受更差的解”,这个概率由温度T控制,T越高,接受差解的概率越大;随着迭代次数增加,T降低,算法逐渐收敛到稳定解。这个“前期允许乱跳、后期逐渐收敛”的思路,在很多优化问题里都有应用。粒子群的核心则是“每个粒子根据自身历史最优和全局最优来更新速度”,惯性权重越大,全局搜索能力越强,越小则局部搜索能力越强。能把这些关键词串起来说清楚,笔试小题基本就能拿分。
5. 踩坑实录:我在笔试中实际遇到的那些送命题
5.1 读题不仔细:把升序看成降序,把非递减看成递增
这是我参加笔试时的真实经历。有一道题题目里明确写了“非递减序列”,我直接当成“上升序列”做,结果一半的测试用例没过。后来我复盘发现,很多同学在笔试中不是不会做,而是读题时漏掉了关键限定词。“非递减”和“递增”差了一个等号,答案差了一个二分查找的边界条件。“最多”“最少”“恰好”这些词的限定,也直接决定你用的是贪心、DP还是二分。读题花30秒,比提交后debug半小时划算得多。
这里给一个实用建议:拿到编程题,先把题目里出现的限定词全部圈出来,尤其注意“非递减/递增”“不重叠/重叠”“最多/最少/恰好”“可以/不可以重复使用”。这些词每一个都可能让解法完全不同。我后来养成了“读完题先在草稿纸上把关键条件抄一遍”的习惯,看似浪费时间,实际上大大降低了写错方向的风险。
5.2 时间复杂度估算失误:O(n^2)在10^5面前就是TLE
有一道题我一开始用O(n^2)写的,本地样例全过,提交后直接TLE。原因很简单:n=10^5,O(n^2)=10^10,在只给2秒的OJ上是绝对跑不完的。这是算法岗笔试最高频的“送命题”。我后来养成了一个习惯:看完数据范围,先估算复杂度,如果O(n^2)过不了,立刻去想O(nlogn)或O(n)的解法,绝不在暴力解上浪费时间。
有一个快速判断方法:1秒大概能跑10^8次简单运算,2秒就是2×10^8。如果n=10^5,O(n^2)是10^10,超了100倍;如果n=10^4,O(n^2)是10^8,勉强能过;如果n=10^6,O(nlogn)大约是2×10^7,很轻松。每次写完代码,先拿这个标准卡一下自己,可以有效避免“本地跑得动,OJ超时”的尴尬。
5.3 KMP的next数组定义混乱:同一道题可以有两套答案
我在第2章提过,next数组有两种常见定义。笔试时如果题面没给公式,纯粹说“计算next数组”,那它可能采用我们常用的“最长相等前后缀长度”定义;但如果题面里写了“next[i]表示失配时跳转的位置”,那数组的值会整体不同。我见过很多同学因为没看定义,直接套模板,白白丢了一道送分题。所以遇到这类概念题,第一件事永远是确认题面中的定义。
如果你在网上刷题,你会发现不同平台的KMP模板,有的返回next数组时第一位是-1,有的第一位是0,甚至有的把数组取名为prefix、pi、fail。这都不是Bug,而是定义不同。备考时最好把两种定义都准备一套写法,并且能快速转换,这样无论在哪个平台笔试都不会慌。
5.4 优先队列的大顶堆陷阱与Dijkstra的负权坑
有一道与Dijkstra相关的题,我在考场上因为贪图快,直接用了一个普通的数组来保存距离,导致时间复杂度飙升。而堆优化的时候,如果不小心把优先队列当成大顶堆用,取出的节点永远是距离最大的那个,整个算法会得到一个错误答案,而且本地小数据样例还看不出来。这是我在实际写代码时踩过最深的坑之一。优先队列存距离时一定要记得用“负值取反”或者自定义比较器。
另外还要注意Dijkstra的适用边界:负权边出现时,Dijkstra直接失效。我当时在复习时专门做了个对比实验,用一个带负权的三节点图去跑Dijkstra,结果发现它给出了一个错误的“最短路径”。这个实验让我彻底记住了Dijkstra的适用范围,比死记结论有效得多。笔试里出现“负权边”“负环”这些词时,基本可以断定考的是Bellman-Ford或者SPFA。
5.5 二分查找死循环:一个看似无解的Bug
二分查找中还有一个经典坑:当更新区间时用了left = mid和right = mid,如果区间长度收缩到1时,就会陷入死循环。正确的做法是,在left = mid的场景下,计算mid时要向上取整:mid = (left + right + 1) // 2。这个细节,笔试不会直接考,但如果你在编程题里写了二分查找,它极有可能成为隐藏的Bug来源。
我记得有一次做“旋转数组的最小值”这类题,就因为没注意向上取整还是向下取整,本地测试死循环了十分钟,最后才发现是mid的计算方式导致left永远不会推进。从那以后,我总结出一个口诀:如果你在二分里用了left = mid,那就把mid向上取整;如果你用的是left = mid + 1,那mid向下取整。这个口诀帮我解决了一类非常容易卡住的Bug。
6. 复盘后的备战建议:如果让我重新准备一次
6.1 夯实基础:数据结构是算法岗的底线
算法岗笔试到最后拼的几乎都是数据结构的基本功。数组、链表、栈、队列、哈希表、树、堆、图,这八大结构必须能随手写出代码。我个人建议把每个数据结构的基本操作(插入、删除、查找、遍历)都整理成自己的模板,笔试时直接调用,而不是现场去推。
比如树的三种非递归遍历,我建议至少写一遍完整的实现。为什么很多同学觉得树的非递归遍历难?因为要用栈模拟递归的过程,一旦写错就容易死循环。但如果平时把模板整理好了,笔试就能直接默写。我当时整理了一整套模板,包括:链表反转、快排、归并、二分查找、堆排序、Dijkstra、KMP、并查集、拓扑排序,考试时遇到类似的题,直接往模板上套,省下大量思考时间。
6.2 系统刷题:不在于多,而在于归类
刷题这件事,量确实重要,但更重要的一定是归类。我建议按“题型”刷而不是按“难度”刷:贪心、二分、双指针、动态规划、图论、字符串、数学,每类题型刷透20~30题,总结出这类题的常见套路。比我当初漫无目的地按LeetCode题号顺序刷,效率高得多。
这里分享一个我后来一直在用的刷题方法:每做完一道题,不急着刷下一道,而是在题目的注释里写下:“这道题属于什么模型?关键突破口是什么?如果题目改一个条件,解法会怎么变?”这样做三遍以上,你会发现很多题目其实是同一个模型的马甲。比如“接雨水”和“柱状图最大矩形”,一个用单调栈,一个也可以用单调栈;比如“最长上升子序列”的贪心二分优化,和“俄罗斯套娃信封”问题几乎一模一样。
6.3 笔试环境模拟:从会写代码到会考试
有一件很多同学忽略的事:笔试环境和你平时写代码的环境差别很大。牛客网的笔试系统大多是ACM模式,需要自己处理输入输出,而LeetCode是核心代码模式,只写函数就可以。差距看起来不大,但一旦你习惯了LeetCode的输入输出,到了牛客网笔试时,光是处理输入格式就能耗费大量时间。建议提前去牛客网刷几套真题,适应一下“读入多组数据”“处理空行”“判断EOF”这些细节。
我自己就吃过这个亏。第一次在牛客网上做模拟笔试时,一道很简单的“两数之和”题目,我因为不会处理input().strip()的边界情况,提交了三次才过。从那以后,我把常用的输入输出模板单独存了一个文件,包括:读整数、读一行数组、读多行,每个模板都测过,笔试直接复制粘贴,基本不会出错。
6.4 算法原理深度:机器学习部分别只背公式
猿辅导这类互联网公司的算法岗笔试,机器学习考点一般不会太深,但一定会考“理解”。与其死记硬背KNN、K-Means、贝叶斯的公式,不如理解每个算法的前提假设、适用范围、优缺点。笔试选择题最喜欢出的就是“下列哪种场景下KNN不适用”这类需要理解的问题。
我备考时做了一个很笨但很有用的练习:拿一张白纸,不看任何资料,把每个算法的流程、公式、适用场景、优缺点写出来,然后和资料对照,补上遗漏的点。这样做一轮之后,我对K-Means和KNN的区别、决策树和信息增益的关系、朴素贝叶斯为什么“朴素”这些概念都清晰了很多,笔试遇到相关题目基本不用犹豫。
印象很深的是,这场笔试让我意识到一件事:算法岗笔试真正的分水岭,不在于你掌握了多少奇技淫巧,而在于基础是否扎实、读题是否仔细、复杂度估算是否准确。很多同学把大量精力花在钻研难题上,结果在KMP的next数组定义、Dijkstra的适用边界、二分查找的边界条件这些基础细节上翻车,非常可惜。
如果让我给准备秋招的同学一个最实际的建议,就是从现在开始,每周固定做两三场完整的模拟笔试,严格按照考试时间来,然后认真复盘每一道错题。光看不练永远不知道自己哪里不会,只有真正在限时压力下写过代码、踩过坑、改过Bug,笔试题对你的杀伤力才会真正降下来。