
1. 从一张卷子反推2018年算法校招想筛选什么样的人2018年秋招那阵子我在笔试现场拿到这份算法工程师试卷时第一反应是“题量真大”。后来才明白这种高密度、强覆盖的卷子不只是考你会不会而是在用最短的时间筛掉一批人。做过几年算法岗面试后再回头看这份试卷它的出题逻辑其实非常清晰基础知识要扎实机器学习要懂原理工程能力要过关代码要写得出来。很多同学在网上找到这份试卷第一反应是到处搜答案想把每道题背下来。但我的建议是先把这张卷子当成一面镜子照一照“2018年一家电商公司的算法团队想要什么样的人”再针对性准备。本文就说清楚这份试卷背后的考察逻辑并拆解每一类题型的核心知识点和备考方法。适合正在准备算法岗校招的同学、想转行做算法的工程师以及带新人的算法负责人参考。我们先把这份试卷的“骨架”还原出来。通常这类校招笔试题型分布如下考察模块大致占比常见题型备考优先级数学与基础算法30%左右选择、填空、简答KMP、排序、树、图极高属于“送分题”和“基础分”机器学习与深度学习30%左右概念题、公式推导、场景应用极高2018年前后尤其看重手推公式大数据与工程认知15%左右MapReduce、Spark、特征工程、数据倾斜中等区分度较大编程题25%左右在线OJ、白板编程DP、BFS/DFS、字符串处理极高直接决定能否进入面试这个结构在当时并非个例。2018年正处于深度学习全面普及、大数据技术栈走向成熟的节点企业既希望候选人懂模型原理又希望他们能落地到分布式环境里跑数据所以笔试里才会同时出现“手推损失函数梯度”和“Spark中的数据倾斜怎么处理”这类横跨理论与工程的题目。换句话说这份试卷考察的不只是知识点而是你有没有建立一套从数据到模型再到业务的完整思维链路。2. 基础算法题KMP、排序与数据结构的“熟而不深”陷阱2.1 一道KMP题就足以暴露“背答案”和“真理解”的区别在热词列表里有一道非常典型的题“对于模式串 p‘abacaba’其 next 数组定义为……” 很多同学看到“next数组”就开始背KMP的模板。但我要提醒你这种题恰恰是出题人用来区分“背模板”和“真理解”的。先明确一个容易混淆的定义在KMP算法中next数组通常有两种定义方式。一种定义是next[i]表示模式串前 i 个字符组成的子串中最长的相同前后缀长度。这是一个“长度值”常用于生成“部分匹配表”。另一种定义是失配后指针应该跳转到的位置即next[i] 最长相同前后缀长度但在实际代码里有的教材会做“减一偏移”或“整体右移”的处理。如果试卷上没有特殊说明通常考察的是第一种next[i]表示子串p[0...i-1]即前 i 个字符中最长相同前后缀的长度next[0]统一取 -1 或 0。我们来手推模式串p abacaba子串长度最长相同前后缀next值按长度定义空串0无next[0] 0或-1a1无next[1] 0ab2无next[2] 0aba3anext[3] 1abac4无next[4] 0abaca5anext[5] 1abacab6abnext[6] 2abacaba7abanext[7] 3如果按“失配跳转位置”的常见代码写法整体右移再补 -1数组会变成[-1, 0, 0, 1, 0, 1, 2, 3]这类形式。题目里如果写的是“next[i] 定义为”务必看清它的基准是长度还是跳转位置很多考生就是在这里丢分。如果你只是记住了KMP的代码模板遇到“求next数组”的填空题还能应付但遇到“为什么复杂度是O(mn)”“为什么不能用朴素算法在字符串中查找多个模式串”这类变体就懵了。KMP的核心思想是当发生失配时已经匹配过的部分里前缀和后缀有重叠利用这个信息跳过不必要的重复比较。举个例子匹配到abacab时发现下一个字符不匹配此时不需要回到模式串开头重新比较因为ab已经在当前文本的尾部出现过直接从模式串的第3个字符即a后面开始继续比较即可。我在实际业务里用到KMP的场景不算多但它作为“理解字符串匹配本质”的入门题在校招笔试中出现的频率极高。备考时不要只背模板找一个断点自己动手画一遍匹配过程比刷十道同类题都管用。2.2 排序算法不只是写出来还要知道为什么选它每个算法岗位的笔试卷里排序算法都会反复出现。热词列表里无论是“冒泡排序算法c”“堆排序算法”还是“快速幂算法c”本质上都在提醒你基础算法的手写能力是绕不开的。排序算法题通常有以下几种考法手写快排、归并、堆排的完整代码给出一个序列写出某排序算法每一趟的结果问某个排序算法是否稳定、最好/最坏/平均时间复杂度在特定场景下选排序方案如“内存不足如何对1TB文件中的整数排序”。这里给出常见的排序算法对比方便快速回顾排序算法平均时间最坏时间空间稳定性适用场景冒泡排序O(n²)O(n²)O(1)稳定教学演示基本不用快速排序O(n log n)O(n²)O(log n)不稳定通用排序C std::sort的底层基础归并排序O(n log n)O(n log n)O(n)稳定外部排序、链表排序堆排序O(n log n)O(n log n)O(1)不稳定TopK、优先队列笔试中经常出现的场景题是“从海量数据中取最大的K个数”。最直接的方案是用堆维护一个大小为K的小顶堆每来一个数就与堆顶比较大于堆顶就替换并调整堆。时间复杂度为 O(n log K)而全排序需要 O(n log n)当 n 远大于 K 时差距非常明显。如果K也很大的情况可以考虑先用位图或者分桶预处理再在每个桶内排序汇总。我在带人时发现一个共性问题很多人能写出快排但说不清“为什么快排平均是O(n log n)而最坏会退化到O(n²)”。答案是分区点选择的随机性问题——如果每次选到的是最小/最大值分区就会退化成一边倒递归深度变成n。这也是为什么工程实现里会引入“三数取中”或者“随机化”来避免最坏情况。这类“为什么”比“怎么写”更能拉分。2.3 图论与贪心高频中的高频热词列表里还有一批图论和优化类算法Dijkstra、二分图HK算法、Kahn算法、贪心算法、模拟退火、粒子群、剪枝等。在校招笔试中Dijkstra和贪心是出现频率最高的。Dijkstra考察的多是“在非负权图中求单源最短路径”。注意它的前提条件图中不能有负权边。如果面试官追问“为什么”你要能说出原因是它依赖“当前最短路径不会再被更新”这一性质而负权边会破坏这个假设。遇到有负权边的场景应该用Bellman-Ford或者SPFA。多数人只记住了堆优化的Dijkstra模板却忽略了前提条件这在笔试和面试中都容易被“挂掉”。贪心算法的难点在于证明“贪心策略为什么是对的”。典型的例子是“活动选择问题”给定若干区间选出最多不重叠的区间贪心策略是按结束时间排序依次选择不冲突的区间。这类题想拿满分不仅要答出贪心策略还要说明为什么这样的局部最优能推出全局最优。在笔试卷里这类题往往以“设计一个算法并证明”的形式出现。我的建议是基础算法部分的复习以“理解原理 手写实现 能解释复杂度”为准不要只追求刷题数量。因为校招笔试考的是“你会不会”而面试考的是“你懂不懂”两份卷子的底层数据结构其实是同一套。3. 机器学习与深度学习题背概念看似会手推公式才见真章3.1 KNN这类模型题可以从三个维度切入作答热词里有“knn算法的应用能力包括哪三个方面”这道题非常典型。KNNK近邻是最基础的监督学习算法之一它的“应用能力”可以从分类、回归、以及异常检测/推荐等场景来回答。分类对未知样本通过距离度量欧氏距离、曼哈顿距离、余弦相似度等找出训练集中最近的K个样本投票决定类别。回归同样找K个近邻但输出是这K个样本标签的平均值或加权平均值。异常检测/推荐当某个样本与其K个近邻的平均距离很大时可以认为是异常点推荐系统里也可以用“与用户行为最相似的K个用户”来做协同过滤。答题时如果只写“KNN用于分类”就太单薄了。可以继续补充KNN是“惰性学习”lazy learning训练阶段不学参数预测阶段才计算距离因此预测耗时和样本量成正比不适合高延迟要求的在线场景。同时它在高维空间中会遇到“维度灾难”——距离度量会变得不再有效这也是为什么在高维数据上常常先做降维再使用KNN。在校招笔试里这类概念题不需要写长篇大论文但要求你能把关键点答全并且能用一个小的例子把逻辑串起来。最好的方式是先在草稿纸上写一个三步结构是什么、怎么用、有什么局限。3.2 “KL ELBO”这类推导题考的是变分推断的逻辑链条热词里出现了“kl elbo 算法原理详解”这其实是变分推断Variational Inference里的核心KL散度与ELBOEvidence Lower Bound证据下界的关系。先给一个简单版本的理解我们希望用分布 q(z) 去近似后验分布 p(z|x)。直接优化KL散度 KL(q(z) || p(z|x)) 很困难因为 p(z|x) 本身是未知的。于是我们转而最大化ELBO因为它等价于“最小化KL散度 常数项”。ELBO写出来是ELBO(q) E_{q(z)}[log p(x, z)] - E_{q(z)}[log q(z)]或者等价形式ELBO(q) E_{q(z)}[log p(x|z)] - KL(q(z) || p(z))这里的第二项是“近似分布与先验分布的KL散度”第一项是“重建似然”。优化ELBO就是在“让模型更好地解释数据”和“让近似后验不要偏离先验太远”之间做权衡。2018年前后这类偏生成模型和贝叶斯方法的推导题出现频率并不低。如果你报的岗位偏向NLP或推荐方向甚至面试官会直接让你在白板上推一版简易VAE的损失函数。VAE的损失函数其实就是“重建损失 KL项”只不过把高斯假设代入后KL散度有了闭合形式的解。你不需要把每个公式背得一字不差但要清楚每一步的数学依据以及每个符号的含义。我理解很多同学觉得“手推公式”是笔试里最痛苦的部分但换个角度想面试官并不是要你当一个计算器而是通过公式推导的过程判断你有没有真正理解这个算法。如果一个人能从头到尾推导出一个逻辑回归的梯度更新过程那他在实际调参时一定比只会调库的人更有方向感。3.3 深度学习与经典模型2018年的考察重心在2018年的笔试里深度学习的比重明显上升。卷积神经网络、循环神经网络、注意力机制、反向传播的链式法则几乎每家公司都会考到。与此同时当年最火的XGBoost也在题目里频繁出现因为这是传统机器学习落地的巅峰期。一道典型的考题是“解释随机森林与GBDT的差异”。可以从以下几个方面回答随机森林是Bagging思路训练多棵决策树每棵树独立训练最后投票或平均GBDT是Boosting思路每棵树拟合前一轮的残差。随机森林的基学习器可以并行训练GBDT必须串行但通常用更浅的树且能处理更复杂的非线性关系。随机森林对异常值更鲁棒GBDT对异常值较敏感因为拟合残差时异常点会产生很大损失。如果题目里再加一层难度会问“XGBoost相比GBDT做了哪些改进”那就需要提到二阶泰勒展开、正则项、列采样、近似分位数分裂等。这些细节在2018年的面经里被反复讨论说明出题人非常看重候选人是否读过原始论文而不只是看过一篇博客。备考机器学习部分我的建议是“以模型为纲以公式为线”。对每一个经典模型至少能回答三件事假设是什么、损失函数怎么写、优化怎么更新。把这个框架整理成自己的笔记笔试时遇到任何模型题都会有一根主线可以顺下来而不是东一句西一句拼凑。4. 大数据与工程认知题这张试卷里最具时代感的那部分4.1 Spark、MapReduce和“数据倾斜”为什么会在算法卷里出现在很多算法岗笔试中大数据模块往往被同学们忽视但它在区分度高的时候恰恰是决定你能不能进面试的关键。2018年的背景是Spark已经很普及但MapReduce的思想仍然是考察大数据认知的起点。一道经典题目是“用MapReduce实现WordCount”。看似简单但考察点在于Map阶段把文本中的每个单词映射为word, 1Shuffle阶段按word做分区和排序把相同的key聚到同一个reducerReduce阶段对同一key的value列表求和。如果你能进一步指出“为什么Shuffle是MapReduce最昂贵的阶段”“如何在Map端做Combine以减少网络传输量”这道题就能从及格变成高分。另一个高频考点是“数据倾斜”。常见场景join操作中某个key的数据量极大导致某个reduce任务成为长尾任务或者group by中某些key特别集中。解决方案不外乎以下几种增大reduce任务数量让负载更分散但这治标不治本对热点key做加盐处理比如拆成多个子key再汇总两阶段聚合本地聚合一次全局聚合一次广播小表避免大表join小表的Shuffle。算法工程师在实际工作中数据清洗、特征拼接、模型训练样本生成每一步都可能遇到数据倾斜。笔试考这个是因为公司希望招进来的人不是“只会写模型训练脚本”而是能处理真实数据管道问题的人。4.2 特征工程与模型上线工程思维是隐藏考点除了分布式计算特征工程也是试卷中反复出现的“软考点”。比如题目问“特征标准化对哪些模型影响大对哪些模型影响小”。答案分两类对基于距离/梯度的模型KNN、SVM、逻辑回归、神经网络影响大。因为特征尺度差异会导致梯度更新方向偏向大尺度特征距离计算也会被大数值特征主导。对基于树的模型决策树、随机森林、GBDT、XGBoost影响小。因为树模型在分裂时只找切分点不依赖特征的具体数值比例。如果在回答时还能补充一句“在深度学习中特征标准化还能帮助优化器更好地收敛甚至在BatchNorm出现后内部协变量偏移问题得到了缓解”那这道题就答出了深度。还有一类工程题是“模型离线效果好线上效果差怎么排查”。这类题的答题框架是离线与在线的特征一致性是否保证训练样本和线上预测样本是否同分布样本选择是否有偏比如只采样了活跃用户数据时间窗口是否过期模型是否定期更新线上特征缺失时的默认值处理是否与训练一致。这些内容在2018年的校招卷子里并不是必考但在面试中问到的概率极高。因为笔试能筛出“懂理论”的人面试才能筛出“能干活”的人。而“能干活”的核心就是具备工程闭环思维。4.3 排序算法与外部排序把“基础”和“工程”串起来在热词列表里“数据结构排序算法”和“堆排序算法”反复出现。很多人以为排序只考手写代码其实还有一个更高级的考法和工程场景强相关外部排序。如果内存不足以容纳所有数据比如要对1TB的整数排序怎么做经典的方案是“外部归并排序”把大文件切分成若干能装进内存的块对每块用快排排序后写回磁盘产生多个有序子文件用败者树或堆从每个有序子文件中取当前最小值归并成一个更大的有序文件重复上述归并直到只剩一个文件。这个方案里就用到第2.2节说的堆。堆排序不仅是面试题更是外部排序和优先队列的底层实现。所以我在复习时总会提醒自己不要孤立地看待知识点笔试里那些看起来毫无关联的“堆排序”“KMP”“MapReduce”在真实业务里往往会通过某种方式连成一条线。当年我在这部分吃亏过以为算法工程师笔试就是写模型、调参数结果遇到“内存限制下做TopK”时第一反应是“用Spark”。但笔试环境里没有Spark只有白板和时间。那之后我把基础知识从头过了一遍才对“算法工程师”这个岗位有了更全面的认识。5. 在线编程题从暴力枚举到AC的临场节奏5.1 看清在线评测平台的“脾气”IO是第一步算法岗笔试的最后一关通常是编程题。很多人理论题答得不错编程题却因为环境问题丢了分。这里说的“环境问题”不是电脑问题而是不熟悉评测平台的输入输出、内存限制和编译规则。常见平台采用“标准输入输出”方式要求从stdin读取结果输出到stdout。也就是说你不仅要写出核心算法还要处理输入解析。比如一次给多行数据每行有多个整数你需要根据第一行的n决定后面读几行或者输入带有逗号分隔的字符串你要先split再转int。这些操作看似简单但非常容易在紧张状态下出错。我的建议是进入笔试前先把目标平台的“输入输出Demo题”刷一遍确保自己熟悉while (cin n)这种C写法或者Python的sys.stdin.read().split()套路。不要等到考场上才第一次写带IO的代码。5.2 拿到题目别急着“最优解”先有“暴力解”在线编程题的时间通常是40到60分钟做2到3道题。很多人犯的错误是一上来就想着最优解结果卡在中间下不了笔最后连暴力分都没拿到。我的经验是先确定暴力解法能拿多少分再逐步优化。比如一道典型的动态规划题“最长上升子序列”。最经典的DP解法是 O(n²) 的定义 dp[i] 表示以第 i 个元素结尾的最长上升子序列长度遍历 i 之前的所有元素更新 dp[i]。如果数据范围 n 1000这个解法完全足够。如果 n 100000则需要用“贪心 二分”的 O(n log n) 解法。笔试时你要快速判断数据范围。如果n很大直接写O(n²)很可能超时但如果没有更好的思路写出暴力和部分DP也能获得部分通过分数。很多OJ采用“按通过的数据点给分”机制部分通过比交白卷强太多。5.3 临场节奏的取舍学会“战略性放弃”编程题往往不止一道份量和难度也不同。我见过不少同学在第一道题上死磕了40分钟结果后两道题连题目都没读完。正确的节奏应该是用2到3分钟读完所有题目判断难度梯度从最简单、最有把握的一道开始写每道题在写完暴力版本后如果时间允许再考虑优化如果某道题卡了15分钟以上没有进展立刻标记并转向下一题。这种“先易后难、写不完也要留痕”的做法听起来很简单但在限时状态下特别考验心态。你可以从现在开始就用OJ做模拟考试严格要求自己按这个节奏走把临场感提前练出来。我自己每次模拟笔试时还会刻意留5分钟回看代码检查数组边界、是否溢出、输出格式是否完全匹配。很多“差一点AC”的题问题往往就出在i写成了i或者循环边界少了一个等号。代码写得对不对靠的是习惯考试时根本没有时间让你复查太多遍。6. 当年没答好的三道题如今回头看全是经验6.1 第一道KMP的 next 数组背错了定义2018年我自己做类似卷子时KMP这一题也曾经被卡住。不是不会求next数组而是我背的那套模板和题目里给出的定义不一致。题目里next[i]是按“长度”定义的我下意识按“跳转位置”写结果整道题从第一个空开始就错了。这件事让我养成了一个习惯任何算法题目先看清楚“定义是什么”再动手。哪怕是背模板背得再熟也要把题目里的变量含义、边界条件先标注出来。这个习惯不仅在校招笔试里有用在后来阅读开源代码、排查线上问题时也同样关键。6.2 第二道手推LR梯度卡在链式法则机器学习部分我当年最怕的就是“手推逻辑回归的梯度”。其实逻辑回归的推导很简单先写出似然函数再取负对数得到损失函数然后对参数求偏导。求导过程中需要用到sigmoid sigmoid * (1 - sigmoid)这个性质最终梯度是(预测值 - 标签) * 特征。但考试时我会慌因为平时都是直接调sklearn的LogisticRegression没有自己推过一遍。后来我花了几天时间把所有常用模型的推导都从头写了一遍包括线性回归最小二乘法、逻辑回归、朴素贝叶斯、SVM的对偶问题甚至把CNN里一层卷积的反向传播手算了。这个过程很枯燥但效果立竿见影——之后再遇到推导题我不需要背因为每一步都能从“为什么要这样算”想明白。6.3 第三道编程题只写了暴力解但拿到了大部分分有一道编程题我至今记得特别清楚是道字符串相关的题目第一反应是用动态规划但我没想清楚状态定义于是先写了一个暴力枚举所有子串的版本。写完暴力版本后我突然想明白状态转移了于是改成DP解法最终AC了。如果当时我因为“暴力解太low”而不肯写可能连验算的机会都没有更别提靠着暴力版本的时间把思路理顺。这个经历后来被我反复讲给团队里的新人代码能力不是“一步到位”而是“先跑起来再变快”。笔试本身就是一场工程实践谁能在有限资源下给出可行解谁就赢了一半。6.4 从备考到实战这套复习框架如今依然适用每次有人问我“算法校招怎么准备”我都会给出一个很朴素的答案不要迷信刷题量而要把每个知识点拆成“是什么、为什么、怎么用”三层。用这份试卷来打比方你要想的不只是“KMP怎么写”而是“为什么KMP能线性匹配”“它在哪些场景下能派上用场”“换一道字符串题我能不能想到类似思路”。如果你想系统训练可以按这个顺序来第1到2周过一遍基础数据结构与排序手写所有经典排序理解复杂度第3到4周集中刷KMP、Dijkstra、DP、贪心、回溯重点是分类总结第5到6周系统整理机器学习经典模型的公式推导配合面经做查漏补缺第7周专项补大数据和工程知识特别是Spark和特征工程第8周做3到5次全真模拟笔试严格限时训练临场节奏。如果你正在准备算法岗校招可以把一份旧试卷当成镜子而不是题库。镜子照出来的是你对算法工程师这个岗位的理解是否完整题库照出来的只是你还有多少题没刷过。这两种思路最终会把你带到完全不同的高度。