2023届秋招那阵子,我连续投了不少算法岗,笔试做了一堆,小满的秋招第二批笔试是其中比较有记忆点的一场。这场笔试不像外界传的“背背八股就能过”,整套卷子把数据结构、机器学习基础、优化算法甚至一些信号处理和控制论的知识都揉在了一起,题型覆盖广、题量不小、难度梯度也明显。这篇文章就来完整复盘当时这场笔试:从题型分布、各题解题思路,到现场踩过的坑、考后的复习路线,一次性写完。准备冲刺算法岗校招笔试的朋友,不管是刚起步还是已经刷了一轮题,都能从中找到可以直接用的东西。
1. 笔试整体设计与考察逻辑
1.1 题型构成与覆盖范围
第二批笔试整体分三块:选择题、手写算法题、简答/推导题。选择题占了大概三成,算法题占四成,剩下的是机器学习和数学基础。这个比例和很多大厂算法岗笔试不太一样,不少公司是选择题占大头、算法题两三道收尾,小满这套卷子反过来,把重头戏放在了代码和推导上,明显是想筛掉只会背题、不会动手的人。
先说选择题。覆盖范围很杂,有纯数据结构概念,比如哈希冲突的常见处理方式、二叉树的不同遍历序关系;也有算法原理层面的,比如快速排序在什么情况下退化成O(n²)、堆排序建堆的时间复杂度、KMP算法中next数组的具体数值。选择题里还混了几道概率统计和线性代数,有一道题问的是协方差矩阵的物理含义,还有个题考了贝叶斯公式的展开,算是比较基础但容易记混的点。
算法题一共五道,难度递进。第一道基本是送分题,字符串去重加排序;第二道是经典的股票买卖问题变种;第三道是带权区间调度,本质是贪心加动态规划;第四道是二维矩阵中的最短路径,我用了Dijkstra的思路去解;最后一道压轴题是文本相似度计算,要求自己设计特征和距离度量。这种题设计思路很典型,前面的题保证大家都有分拿,后面的题拉区分度。
简答/推导题有两道。一道给了个简化的概率图模型,要求推导后验分布的表达式,另一道让写出粒子群算法的速度和位置更新公式,并说明惯性权重的作用。粒子群这道题我当时愣了一下,因为常规刷题很少碰到,但幸好之前看过优化算法的科普,记住了核心公式。这套卷子的设计思路其实很明确:数据结构与经典算法基本功要扎实,机器学习基础概念不能只会名词,还要能做简单推导,同时开放题考察工程落地时的选型和取舍能力。
1.2 时间分配与做题顺序建议
笔试总时长120分钟,题量看起来不大,但实际写起来非常紧。我的建议是先做算法题,再做简答推导,最后做选择题。原因很简单,算法题分值高、思路连贯,一旦进入状态越写越顺,优先拿下大头再说。选择题如果卡住了,可以先标记跳过,等到最后有时间再回来蒙一个,不至于因为一道概念题浪费太多时间。
我当时的时间分配大概是:前60分钟做前四道算法题,每道题控制在15分钟左右,最后一道压轴题留了30分钟,剩下的30分钟分给简答题和选择题。这里有个很关键的技巧,就是每道算法题动手写代码之前,先在草稿纸上把自己的思路、时间复杂度和特殊边界条件列出来,再开始敲键盘。很多同学一上来就写,写着写着发现思路错了,代码删了重写,时间根本不够。笔试不像IDE里有调试器可以用,想清楚再动手,效率高得多。
有个容易被忽略的点,是简答题的书写规范。线上笔试的简答题虽然是文字输入,但建议分点作答,必要的公式用文本形式表达清楚。阅卷的人往往一天看几百份卷子,看到一坨不分段的文字,第一印象就差很多。比如粒子群那道题,我把速度更新公式、位置更新公式、惯性权重的含义分开写,还在后面补了一句“惯性权重较大时全局搜索能力强,较小时局部搜索能力强”,这种细节能明显体现对算法的理解程度。
2. 数据结构与经典算法题拆解
2.1 KMP算法的next数组计算
选择题里有一道KMP算法相关的题,原题大意是给出模式串p = "abacaba",要求写出它的next数组。KMP这个算法在大厂笔试里出现频率很高,因为它考察点很集中:对前缀后缀匹配关系的理解、对数组下标处理的能力,以及代码实现细节。很多同学刷题时能背出KMP主流程,但一到手动计算next数组就翻车,原因是对next数组的定义自己就没搞透。
next数组在不同教材里有两种定义:一种是next[i]表示模式串前i个字符组成的子串中,最长相同前缀后缀的长度;另一种是next[i]表示失配后跳转的位置下标。这两种定义算出来的数组数值不同,但本质思想一样。如果题目没有特殊说明,默认取最长相同前缀后缀长度,我遇到的原题就是这个定义,模式串从下标0开始,所以next[0] = -1,next[1] = 0。
手动推算一遍。对于p = "abacaba":
- next[0] = -1
- next[1],子串"a",没有相同前缀后缀,next[1] = 0
- next[2],子串"ab",前缀a、后缀b不相等,next[2] = 0
- next[3],子串"aba",前缀a与后缀a相等,长度1,next[3] = 1
- next[4],子串"abac",前缀ab与后缀ac不相等,前缀a与后缀c不相等,next[4] = 0
- next[5],子串"abaca",最长相同前缀后缀是"a",长度1,next[5] = 1
- next[6],子串"abacab",最长相同前缀后缀是"ab",长度2,next[6] = 2
- next[7],子串"abacaba",最长相同前缀后缀"aba",长度3,next[7] = 3
所以next数组是[-1, 0, 0, 1, 0, 1, 2, 3]。
计算的时候有个技巧:每次求next[i]时,不要从头开始比较前缀后缀,而是利用next[i-1]的结果继续往后匹配,这就是KMP算法本身优化next数组构建的核心思想。如果笔试时不允许查资料,用暴力法逐个子串求最长相同前缀后缀也能算出来,只是慢一点,胜在不容易出错。
这里提个醒,有些笔试平台给出的模式串下标从1开始,那next[1] = 0,next[2]可能为1。做题前一定要先看清楚题目对下标和next数组的定义,不然直接套公式容易翻车。
2.2 手写排序与排序算法复杂度分析
选择题里有一道排序算法相关的问题,问的是归并排序和快速排序各自的稳定性、最坏时间复杂度和额外空间复杂度。这题考察的是对排序算法本质的理解,而不只是背结论。快速排序的平均复杂度是O(n log n),最坏是O(n²),发生在每次选基准值都选到最大或最小值时,比如对已经有序的数组做快速排序且每次都取第一个元素作为基准。归并排序则不管输入数据如何,时间复杂度都稳定在O(n log n),代价是额外O(n)的空间用来合并临时数组。
冒泡排序这次没出现在代码题里,但选择题有它的一席之地,问的是“在完全有序的数组上,优化后的冒泡排序时间复杂度是多少”。答案是O(n),因为优化版冒泡排序增加了一个标志位,当一轮遍历没有发生任何交换时,说明数组已经有序,直接结束。这个考点很小,但很能看出一个人有没有真正理解冒泡排序的机制。同样类似的还有插入排序,当数据接近有序时,插入排序的效率很高,接近O(n),这是它在实际工程中常用于小规模数据排序的原因。
手写代码题第一道就是排序变种。题目要求是给一个字符串数组按长度升序排序,长度相同的按字典序升序。这个直接用Python的sort函数传key参数就能一行解决,但笔试考官的意图显然不是让你调库,而是要看你对稳定排序的理解。sort在Python里是稳定排序,底层是Timsort,所以顺序相同的元素会保持原始相对顺序。如果拿C++也简单,std::sort本身不稳定,需要给元素加上原始下标,用自定义比较函数来保证同长度时的字典序。我当场用了Python一行代码跑通,复杂度O(n log n),稳过。
2.3 贪心、Dijkstra与堆排序的组合考法
第二道算法题是股票买卖的变种,题意是可以完成多次交易,但卖出后第二天不能买入,也就是存在冷却期。这不是单纯贪心能解的题,需要动态规划,定义三个状态:持有股票、不持有且处于冷却期、不持有且不在冷却期,然后按天数递推。代码量不大,但有细节,容易漏掉状态之间的转移边。这道题我在笔试时先写了贪心解法,忽略了冷却期条件,提交前自己模拟了个反例,发现贪心会得到错误结果,马上改成DP,所以笔试时一定要留出时间验证反例。
第三道带权区间调度题,是经典的贪心加动态规划模型。给定n个区间,每个区间有开始时间、结束时间和权重,要求选择一组互不重叠的区间,使得总权重最大。这题的解法是先按结束时间排序,记录每个区间之前最后一个与它不冲突的区间下标p[i],然后DP递推:dp[i] = max(dp[i-1], dp[p[i]] + weight[i])。我第一次接触这个题时觉得和普通区间调度差不多,直接按结束时间贪心选最早的,但带权时贪心不成立,必须DP。笔试时我花了几分钟推导递推式,确认状态转移无误后写代码,一次通过。
第四道是二维矩阵最短路径,矩阵中每个格子有正权值,要求从左上走到右下,只能向右或向下走,求最小路径和。这个题用DP就够了,但原题多加了一个限制:某些格子有额外消耗,且移动方向可以上下左右,这时候DP失效,得用Dijkstra。我按Dijkstra实现,用优先队列优化,把每个格子的最小消耗作为优先队列的优先级,每次弹出当前消耗最小的节点,向四个方向扩展,直到到达终点。复杂度O(mn log(mn)),m和n是矩阵行列数,在笔试数据规模下完全跑得动。
Dijkstra算法在热词里和堆排序经常一起出现,原因很实际:优先队列就是堆实现的。笔试时如果只记得Dijkstra思想而不会用堆优化,很容易超时。我在代码里直接用了heapq,Python的heapq就是最小堆,入堆和出堆都是O(log n),比每次线性找最小点快一个数量级。
2.4 快速幂与字符串类题目的细节坑
前四道算法题里有一道没有直接考排序,而是考了数值计算:实现快速幂。原题是计算a^n mod m,n可以大到10^18。这题思路简单,把指数n转成二进制,从最低位开始处理,每次结果平方,如果当前二进制位是1,就把结果乘上当前底数。核心代码长这样:
def fast_pow(a, n, m): result = 1 a = a % m while n > 0: if n & 1: result = (result * a) % m a = (a * a) % m n >>= 1 return result这种题考察的不是会不会用内置函数,而是对位运算和快速幂原理的理解。很多人能写出来,但容易漏掉两点:一是底数要先取模,防止a本身很大;二是乘方过程每一步都要取模,否则中间结果溢出。C++选手尤其要注意,long long都可能被中间结果爆掉,必须边算边取模。Python玩家没有溢出问题,但大整数运算慢,遇到超大指数时按位处理反而更稳定。
考后我复盘时发现,快速幂这个知识点在秋招里出镜率很高,很多厂的笔试都会混在某个题里当子步骤。比如矩阵快速幂是常考题,用来快速计算斐波那契数列第n项,复杂度O(log n),n到10^18也不怕。如果你还没掌握矩阵快速幂,建议把快速幂吃透后顺手推一遍,逻辑只是把底数a从整数换成矩阵,把乘法换成矩阵乘法,本质上没有区别。
3. 机器学习、深度学习与优化算法考点
3.1 概率模型推导:从KL散度到ELBO
简答推导题第一道给了个简单的概率图模型,让推导后验分布。这种题没有标准答案,考察的是概率论基本功和变分推断的理解。核心概念绕不开KL散度,它衡量两个分布之间的差异,定义式是KL(q||p) = Σ q(x) log(q(x)/p(x)),值越小说明两个分布越接近。在变分推断里,我们希望找到简单的近似分布q(z)去逼近真实后验p(z|x),做法就是最小化KL(q(z)||p(z|x)),但这个KL直接算不出来,因为p(z|x)包含难以求解的归一化常数。于是变分推断转向最大化ELBO,也就是证据下界。
ELBO的推导要清楚:log p(x) = ELBO + KL(q(z)||p(z|x)),因为KL恒大于等于0,所以ELBO是log p(x)的下界。最大化ELBO等价于最小化KL,同时ELBO可以写成E[log p(x,z)] - E[log q(z)],这两项都是期望形式,可以通过蒙特卡洛采样估计,所以在实践中可优化。KL和ELBO这个考点在很多算法岗笔试面试里都会出现,尤其做NLP、推荐、生成模型方向的岗位,几乎必考。复习时建议把推导过程手推一遍,光看别人的笔记印象不深。
对于笔试现场而言,这种推导题不需要写出特别严格的数学证明,但关键公式和逻辑关系要写清楚。我当时按“后验难以求解—引入变分分布—最小化KL—等价最大化ELBO”的顺序写,每一步附上关键公式,阅卷人扫一眼就能抓住主线。
3.2 聚类、KNN与BM25:经典模型原理题
选择题里有一道关于K-means的,问的是K-means是否一定收敛。答案是会收敛,但会收敛到局部最优,不同的初始中心会得到不同结果。K-means本质是坐标下降法在平方误差聚类目标上的迭代求解,每次迭代固定中心更新簇分配,固定簇分配更新中心,目标函数单调不减,所以收敛。但目标函数是非凸的,所以收敛点可能是局部最优。类似的讨论也适用于高斯混合模型,只是GMM用的是EM算法迭代优化。这组概念经常放一起考:K-means硬聚类,GMM软聚类,KNN则是懒惰学习的监督分类算法。
KNN的应用能力在热词里有“包括哪三个方面”,常见划分是分类、回归和密度估计。分类就是新样本找最近的K个邻居投票,回归是取K个邻居的均值或加权均值,密度估计是统计每个区域的样本密度用于异常检测等场景。KNN原理简单,但实际用起来坑不少:特征尺度敏感,必须先标准化;高维数据下距离度量失效,欧氏距离区分度下降;预测时计算量大,需要KD树或球树加速。笔试如果问KNN的优缺点,这几个点是拿分关键。
文本相似度作为压轴题出现,自然会联想到BM25算法。BM25是经典的信息检索相关性打分函数,在ES里对标Default Similarity,公式我记忆里是:score(D,Q) = Σ IDF(q_i) * (f(q_i,D) * (k1+1)) / (f(q_i,D) + k1 * (1-b+b*|D|/avgdl)),其中f(q_i,D)是词项在文档中的词频,k1和b是调节参数,一般取1.2和0.75。这个公式看着复杂,核心思想就两条:词频越高匹配度越高,但词频带来的收益是边际递减的;文档越短,同等词频下的匹配度贡献越大。压轴题我设计的方案是用BM25做基础打分,再用编辑距离处理短文本的同义改写,两层结合,效果比单用Jaccard相似度好很多。
3.3 交叉领域:PID、卡尔曼滤波与启发式优化算法
这套卷子还有几道题脱离常规算法岗刷题范围,比如PID算法的基本组成、卡尔曼滤波的预测更新两个阶段。这些在纯互联网公司算法岗笔试里不常见,但小满这边似乎比较看重工程系统能力,出了控制和状态估计方向的题。PID本质是根据偏差的比例、积分、微分三项来调整控制量,参数整定是实践中的核心难点。卡尔曼滤波则是用状态转移模型预测下一时刻状态,再用观测值加权修正,核心是计算卡尔曼增益,增益大则更相信观测,增益小则更相信预测。
还有一道关于粒子群算法的题在上面提过,热词里也出现了模拟退火。粒子群(PSO)的核心公式是:速度更新 v = wv + c1r1*(pbest - x) + c2r2(gbest - x),位置更新 x = x + v。惯性权重w的作用是平衡全局搜索和局部搜索:w大,粒子保持大速度,探索范围广;w小,粒子速度衰减快,容易局部收敛。模拟退火的思路则是从高温开始,以一定概率接受更差的解,温度逐渐下降,概率逐渐趋近于0,最终稳定在最优解附近。这类启发式优化算法在模型调参、特征选择、路径规划等场景都有应用,笔试里出现属于“区分项”,能答出来很加印象分。
我当时在这类交叉题上的策略是,能写多少写多少,核心公式写出来,参数含义解释清楚,用自己的理解说一遍适用场景。就算不能完全答对,也要让阅卷人看到知识面足够广。毕竟算法岗不等于只会刷LeetCode,业务里经常要综合考虑算法选型和落地成本。
4. 实操过程:一道真题的完整解题与实现
4.1 题目描述与思路推演
压轴题我记得比较清楚,因为它和搜索推荐业务关系密切。原题大意是:给一个文档列表和一个查询串,要求返回Top 5最相似的文档,文档由若干词组成,查询串也是若干词。可以自行定义相似度度量,要求给出算法思路并写核心代码。这是典型的开放型设计题,没有标准答案,考的是信息检索基础、特征设计能力,以及代码实现的规范性。
我的解题思路分三步:第一步,对每个文档做分词和词频统计;第二步,选择相似度度量,我用BM25作为基础排序,再叠加一个词语共现的Jaccard系数做辅助;第三步,对查询串逐项打分,取Top 5输出。这道题的关键不是代码本身多复杂,而是能不能在有限时间内把思路和代码都写清楚。
我写到代码时采用了结构相对清晰的设计:
import math from collections import Counter import heapq def bm25_score(query_words, doc_freq, doc_len, avgdl, N, k1=1.2, b=0.75): score = 0.0 qf = Counter(query_words) for word in query_words: if word not in doc_freq: continue df = doc_freq[word] idf = math.log(1 + (N - df + 0.5) / (df + 0.5)) tf = qf[word] norm = (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * doc_len / avgdl)) score += idf * norm return score文档词频、文档长度这些特征需要在主循环里提前统计,然后对每个文档单独计算打分。我当时是用一个二维列表存储每篇文档的实体和分词结果,代码大概60行左右,重点把数据预处理、打分函数、Top K排序三个模块拆开写,避免把所有逻辑堆在主函数里。这样即使有BUG,定位也快。
4.2 代码实现与复杂度验证
写完核心打分函数后,主逻辑就很简单了:遍历文档列表,用BM25打分,把所有分数放进一个最小堆,维护堆大小为5,最后把堆里的5个文档倒序输出。用最小堆代替全排序的好处是复杂度从O(n log n)降到O(n log 5),当文档数量百万级别时差距非常明显。这里其实就是Top K问题的标准解法,笔试和工程里都常用。
我笔试时顺手估算了一下复杂度:预处理每个文档分词并统计词频,假设总词数是M,复杂度O(M);打分阶段遍历N个文档,每次打分要遍历查询词去查词频表,查询词长度一般不超过几十,所以打分阶段是O(N L),L是查询长度。整体线性复杂度,在笔试数据规模下完全没问题。
我还要特别说明一下,笔试时压轴题的输入输出格式要求往往是“第一行一个整数N,接下来N行每行是一篇文档,最后一行是查询串”。这种格式没有第三方库解析,需要自己处理。当时平台支持Python,我直接用sys.stdin.read()读全文,按行切分,避免用input()逐行读导致超时。很多同学在这个环节浪费时间,问题就出在没有提前熟悉笔试平台的输入输出模板。
4.3 笔试现场的输入输出处理与自测
笔试平台和本地环境差别很大,本地跑通的代码贴过去可能各种问题。最常见的是输入输出格式不匹配,比如题目要求每行输出一个结果,你直接print一个列表,格式就错了。我的习惯是每道题写完后,先自己构造两组小样例测试:一组是题目给的样例,一组是自己想的边界数据。边界数据包括空输入、只有一个元素、所有元素相同、极大数值等。这些边界数据往往能暴露隐藏的逻辑BUG。
再一个经验是,笔试时尽量避免用需要额外安装的第三方包。很多平台只提供标准库,numpy这类科学计算库不一定装,保险起见全部用标准库实现。我在写BM25时就全程用math和collections,没有引入numpy,确保平台能跑。如果你想在简历里体现代码习惯好,可以在代码块开头加注释说明依赖技术栈,但不要拿这个代替代码。
还有个小技巧,笔试时间允许的话,可以把核心算法单独抽成函数,然后在主函数里调用。这样写既方便自测时传自己构造的测试输入,也方便阅卷人看到你的代码结构。如果题目要求写类方法,也要保持函数尽量短,职责明确。代码风格和规范在算法岗笔试里同样会被评分,不是只看最终结果对不对。
5. 考后复盘与常见问题排查
5.1 高频失分点与边界条件案例
考完当天我把自己丢分的点整理了一遍。第一类失分是边界条件考虑不全。比如股票带冷却期的问题,首次买入时前一天的冷却期状态要单独处理,直接套递推公式会把第0天的状态拿错。第二类失分是复杂度分析没写清楚。很多笔试题要求“请说明你的算法复杂度”,有些同学代码写对了,但复杂度分析写错了,比如把Dijkstra分析成O(n²),忘记写优先队列优化后的O(m log n),白白丢分。第三类失分是概念题用自己理解的口语写,没有写关键公式。比如问快速排序时间复杂度,必须写“平均O(n log n),最坏O(n²)”,而不是只写“挺快的”。
边界条件这块,我总结了一张自查清单,笔试每道题提交前都可以照着过一遍:
| 检查项 | 典型例子 | 应对方法 |
|---|---|---|
| 空输入 | 字符串为空、数组长度为0 | 开头单独判断 |
| 单元素 | 只有一个字符/一个节点 | 保证不越界 |
| 重复元素 | 数组全部相同 | 排序/去重时确认逻辑 |
| 极值 | 数值最大或最小 | 避免溢出、类型转换 |
| 有序输入 | 输入本身有序 | 确认算法不会退化 |
| 输入格式 | 多组测试样例 | 正确处理读取循环 |
笔试不同于面试,没法把代码放回IDE里单步调试,所以在一开始就要养成“防御式编程”的习惯。与其赌边界不出问题,不如每道题都先写边界判断。花不了几行代码,但保住的可能是整道题的分数。
5.2 复杂度超标:常见优化手法总结
另一类高频问题就是代码逻辑正确但超时。笔试平台的运行时间限制一般1到2秒,数据规模一大,O(n²)基本必挂。我在秋招刷题时总结了几条优化路径,按优先级排列:第一,如果你写的是两层循环,看能不能用哈希表把内层查找降成O(1);第二,如果是有序数组上的查找,考虑二分;第三,如果是求Top K,用堆而不是全排序;第四,如果是排列组合遍历,考虑剪枝或DP,是不是重复状态太多;第五,如果是图相关,检查有没有用空间换时间,比如visited数组。
这次笔试第四题矩阵路径,我一开始想用纯DP,发现题目允许上下左右四个方向移动后,DP的“无后效性”条件不满足了,果断切到Dijkstra。这个切换过程其实就是复杂度和算法模型的权衡:能用DP就用DP,DP不能用再上图的算法,优先把约束条件搞清楚。做题最怕的是一开始就套模板,看到“最短路径”四个字直接写Dijkstra,没有意识到这题的方向限制可能用不上图算法。模板有用,但理解题目约束才是王道。
5.3 笔试平台与环境的坑
还有一类完全和技术无关的坑,不值得丢分但很多人真会踩。一是浏览器崩溃。笔试前一定记得关掉多余标签页,把网页版IDE的自动保存打开,每写完一题手动保存一次。二是网络波动。遇到过两次代码提交时网络超时,结果没传上去,平台会记录交卷时间,超时就尴尬了。三是输入法问题。写代码时一定切英文输入法,不然标点符号会变成中文全角,轻则编译失败,重则一下没发现浪费好几分钟。四是本地环境差异。有些人习惯本地Python3.12的新语法,平台还是Python3.8,容易语法报错,写代码时尽量用兼容性高的写法。
这些看起来是小事,但在限时笔试里,心态一旦被这些环境问题搞崩,后面题目思路全乱。我的经验是,考试前先花五分钟做平台给的“环境测试”,模拟一次提交,确认代码能正常编译运行,再进入正式笔试。这笔时间花得值,相当于给自己的考试过程买了份保险。
6. 后续复习路线与个人体会
6.1 刷题优先级与题库选择
考完这套题之后,我重新调整了秋招复习计划。第一阶段,先把数据结构基础过扎实:数组、链表、栈、队列、哈希表、树、图、堆。很多题表面花里胡哨,扒开内核就是这些结构的操作组合。第二阶段,死磕经典算法模型:二分、排序、双指针、滑动窗口、贪心、DP、DFS/BFS、Dijkstra、KMP。这些是笔试出现频率最高的类型,每种至少要能熟练手写模板,还要知道变种。第三阶段,才是机器学习算法原理和数学推导,包括KNN、K-means、聚类、GMM、EM、KL散度、ELBO这些面经高频点。
刷题平台我建议按专题刷,而不是漫无目的地做每日一题。比如这周只刷动态规划,下周只刷图算法,每个专题刷3天就换下一个,循环两轮。相比随机刷题,专题刷题的好处是能快速形成框架思维:看到题目关键词,就能联想到属于哪个专题、可能用哪种解法。这也是我在笔试现场看到“带权区间调度”能几秒钟定位到“按结束时间排序+DP”的原因。
6.2 算法岗笔试与面试的衔接
笔试不只是笔试,面试官经常在面试环节拿着你的笔试代码提问。我后来面试时就被问过BM25那道题:为什么选择BM25而不是TF-IDF,k1和b参数怎么调,如果文档量特别大怎么优化。这些问题不提前准备,当场很容易被问住。所以笔试结束后不要急着把题抛到脑后,建议把每道题整理成一个小文档,写清楚自己当时的解法、可以优化的方向,以及和业务的关联。这套复盘文档,既是对知识框架的巩固,也是后续面试的素材库。
我个人的体会是,算法岗秋招笔试越来越不满足于“会写LeetCode原题”,而是更看重算法功底、扩展知识面、工程代码习惯的综合判断。小满第二批这套卷子很典型,它用五六道题把“数据结构与基础算法—机器学习原理—工程实战能力”三层能力都测了一遍。如果你正在准备类似的笔试,最有效的方式就是:刷题保证手感,推导保证深度,代码保证规范。三者结合,笔试这一关才走得稳。