快手2020校招算法C卷复盘:KMP、Dijkstra与机器学习考点全解析 快手2020校园招聘秋招笔试——算法C试卷2020届秋季招聘刚启动的时候我印象最深的不是各家大厂官网上的宣传视频也不是宣讲会现场排得老长的队伍而是快手笔试系统倒计时页面那个每分钟都在跳动的数字。算法岗的C卷我记得当时规定时间是90分钟题量不算夸张但覆盖面相当宽——从数据结构、经典算法到机器学习基础再到两到三道编程题基本把“算法工程师”这个岗位需要的能力底仓全部扫了一遍。很多同学考完出来吐槽说“像是参加了一场数据结构期末考加LeetCode周赛的混合体”这话虽然夸张但方向确实是那么回事。今天就把我当时拆解这份试卷、准备考点、以及在实战中踩过的坑完整复盘一遍给后面要投算法岗、机器学习岗的同学做一个参考。如果你准备的目标也是大厂算法岗快手这份C卷的参考价值其实很高它不是那种纯LeetCode刷题堆出来的卷子而是把“算法功底”和“机器学习基础”揉在一起考。你光会刷题不行你得能说清楚K-Means和KNN的区别你光会调参也不行手写个快速幂或者处理个链表边界条件就能把你卡住。适合谁来读两类人最值得读一类是马上要参加秋招的应届生另一类是想系统检查自己算法功底的在校学生。我会从考点分布、经典算法细节、机器学习高频概念、编程题实战策略、备考节奏这五个维度展开尽量把我能回忆起来的考试细节和经验都写清楚。1. 快手算法C卷在考什么先认清战场再谈刷题1.1 试卷定位与题型结构从90分钟倒计时看考察逻辑快手2020届校招的算法岗笔试分为多套试卷C卷是其中比较典型的一套整体定位是机器学习/算法工程师方向。和纯后端研发试卷的明显区别在于C卷里面除了数据结构与算法还有相当比重的机器学习基础、概率统计题目。我当时拿到的试卷结构大致是单选题10道左右多选题5道左右编程题2到3道。选择题覆盖的知识点很散从排序算法复杂度、KMP的next数组、Dijkstra的适用条件到SVM核函数、K-Means的收敛性、强化学习里的探索与利用几乎每个方向都会沾一点。为什么这样设计我后来和做校招命题的朋友聊过他们说算法岗笔试的第一目标不是招到“最聪明的人”而是用最低成本筛掉“基础不牢的人”。因为后续还有面试面试官可以当面考察思维深度但笔试必须先把那些数据结构没学扎实、机器学习概念混淆、代码能力不过关的候选人过滤掉。所以你会发现这份试卷里的选择题往往不难但特别考验“是否真的理解”而不是“是否背过”。比如同一道排序题它会换一种问法给定一个几乎有序的数组用什么排序算法效率最高这就不是单纯背“快排平均O(n log n)”能答对的。从时间压力来看90分钟要做完十几道选择题加两到三道编程题平均每道题的思考时间非常有限。这意味着你必须在看到题目的前30秒内判断出考点然后快速给出答案或写出代码。很多同学挂在选择题上不是因为不会而是因为在一道多选题上反复纠结导致最后编程题只剩20分钟。我当时的策略是选择题单题超过2分钟直接标记跳过先把编程题做完再回头补。这个策略在后面会详细展开。1.2 算法C卷与研发卷的差异为什么算法岗要额外考机器学习有不少同学会拿快手研发岗的笔试题和算法C卷对比想知道能不能“一套准备吃遍所有岗位”。我的建议是如果你投的是算法岗一定要单独准备不要觉得刷LeetCode就够了。研发岗的试卷核心是数据结构与算法考到树、图、动态规划的概率极大算法C卷虽然也考这些但侧重点明显不同。举个具体的例子同样是考字符串匹配后端卷可能直接让你实现一个KMP的匹配过程考察代码能力和边界处理算法C卷则可能以选择题形式出现给出一个模式串比如“abacaba”让你手算它的next数组或者让你判断KMP相比暴力匹配在哪些场景下优势不明显。这就考察的是“是否真正理解算法内部的机制”而不是“能否把代码默写出来”。再说机器学习这部分是研发卷基本不涉及的。算法C卷的机器学习题拿分性价比其实很高因为它们考的都是经典模型的核心概念比如K-Means的类中心更新、KNN的k值选择、朴素贝叶斯的条件独立假设、SVM的核函数作用等等。这些概念只要系统学过一轮基本都能答对。但问题在于很多同学是“算法岗的简历研发岗的准备”——主攻刷题机器学习只看了个皮毛结果笔试遇到“过拟合的解决手段有哪些”这种多选题四个选项排除不掉。我在备考时总结了一个公式算法C卷的复习权重大概是“数据结构与经典算法40% 机器学习基础30% 编程题实战30%”。这个比例不是随便拍的而是根据我考完后回忆题量估算的。选择题里真正常规算法题只有三分之一左右剩下的是机器学习、概率统计、甚至少量控制论相关内容。如果只看算法最多只能拿到70%的分数而笔试入围线通常划在75%左右这就逼着你必须补齐机器学习基础。2. 经典算法考点逐个击破从next数组到图论最短路2.1 KMP的next数组到底怎么手算以abacaba为例的完整演算KMP算法是算法笔试的高频考点几乎每一场大厂笔试都会碰到。快手C卷当年直接在选择题里给了模式串“abacaba”让考生求某个位置的next值很多同学在这里翻了车。其实next数组的计算方法并不复杂但有两个容易混淆的概念必须先理清一个是“最长相等前后缀的长度”另一个是“失配后跳转的位置”。不同教材对next数组的定义略有差异有的next[i]表示前i个字符组成的子串的最长相等前后缀长度有的则表示失配时模式串指针跳转到的下标。考试时一定要先看题目给的定义否则结果完全不同。以“abacaba”为例我按最常见的定义next[i]表示前i个字符构成的子串的最长相等前后缀长度手算一遍next[0] -1或0看具体定义i 1子串“a”没有真前后缀next[1] 0i 2子串“ab”前缀“a”不等于后缀“b”next[2] 0i 3子串“aba”前缀“a”等于后缀“a”前缀“ab”不等于后缀“ba”所以最长相等前后缀长度为1next[3] 1i 4子串“abac”前缀“a”不等于后缀“c”next[4] 0i 5子串“abaca”前缀“a”等于后缀“a”前缀“ab”不等于后缀“ca”next[5] 1i 6子串“abacab”前缀“ab”等于后缀“ab”所以next[6] 2i 7子串“abacaba”前缀“aba”等于后缀“aba”所以next[7] 3这样整个next数组就是[-1, 0, 0, 1, 0, 1, 2, 3]如果next[0]按-1处理。我当时在考场上没有专门去记这个数组而是用“前后缀指针移动”的方式来手算也就是把模式串写下来用一个指针从前往后、一个指针从后往前对比逐个推出。这个方法在时间紧张的时候很管用不容易出错。这里有一个实操建议准备笔试时KMP你一定要亲手在纸上推至少三个不同模式串的next数组不要只在IDE里跑通代码。因为笔试是纸笔或系统答题你没法运行代码验证只能靠手算。我推过的三个串分别是“abacaba”、“aaaab”、“abcababc”把这三个推熟基本上next数组的各种情况都覆盖了。2.2 Dijkstra与贪心策略最短路问题的两个“为什么”Dijkstra算法也是份卷子里很可能出现的考点而且经常不是让你直接写代码而是考“为什么Dijkstra不能处理负权边”或者“Dijkstra和贪心策略的关系”。这两个问题直击算法本质很多同学知道Dijkstra能用却说不清它为什么能用。Dijkstra的核心思想是贪心每次从未确定最短路的顶点中选择距离源点最近的一个然后松弛它的邻边。这个策略之所以能得到全局最优是因为在非负权边的约束下当前距离源点最近的那个顶点它的最短路一定已经确定了——因为任何绕路的路径都会引入非负的额外权重不可能比当前距离更短。这就是Dijkstra正确性的关键。如果存在负权边这个“绕路不会更短”的假设就不成立了你可能会在后期找到一个更短的路径所以需要Bellman-Ford或者SPFA来处理。我在做题时还总结过一个面试官常问的变体“如果图中所有边权都相同Dijkstra 的优先队列实现是不是等价于 BFS”答案是等价且可以退化为普通队列。这个考点在笔试里不一定直接考但如果你在面试阶段被问到这就能体现你对算法理解的深度。关于Dijkstra的堆优化实现笔试编程题里如果出现最短路数据范围通常会上到10万个节点O(n²)的朴素实现大概率超时必须用优先队列优化。我当时踩过一个坑用Python写优先队列默认是小顶堆但Dijkstra需要按距离从小到大取顶点所以正确做法是把(距离, 节点)作为元组放入堆中同时用visited数组标记已经确定最短路的节点。如果不加visited标记同一个节点可能被重复入堆多次虽然结果还是对的但效率会打折扣。2.3 排序与复杂度陷阱笔试选择题里的“看起来都对”排序算法是选择题的常客而且往往是最容易丢分的地方。不是因为难而是因为选项设计得太有迷惑性。比如给你一个长度为100万的数组数组元素取值范围只有0到100问用什么排序算法最快很多人一看这个规模就选快速排序但正确答案应该是计数排序因为取值范围小计数排序的复杂度是O(n k)k是取值范围在这个场景下是O(n)比快排的O(n log n)快得多。类似的陷阱还有“稳定性”问题如果题目问稳定排序有哪些你要能立刻列出冒泡、插入、归并、基数如果问哪些排序不适合用链表实现希尔排序和堆排序都要排除因为它们依赖随机访问。堆排序这个点也容易考细节建堆的时间复杂度为什么是O(n)而不是O(n log n)因为从最后一个非叶子节点往前做向下调整虽然每次调整是O(log n)但越往上的节点越少整体摊还下来是O(n)。这个“为什么”就是区分“背过结论”和“真的懂”的试金石。我自己在复习排序时做了一个对比表把七个主流排序算法的平均/最坏复杂度、稳定性、额外空间放在一张表里考前看一遍考场上基本能秒答。笔试不要求你证明复杂度的推导过程但要能快速回忆并应用。2.4 快速幂与位运算边界条件最容易翻车快速幂在算法笔试里出现频率不低尤其是在“求大数幂的模”这一类题目里。很多人能背出递归版本的代码但对边界条件处理不到位。比如求a的n次方模m如果n是0返回1对m取模如果n是负数要先把a转为逆元再幂运算。笔试编程题一般不会让你处理负数但n为0的边界一定有。快速幂的另一种考法是在选择题里考它的时间复杂度O(log n)。这个结论要能说明白——每次把指数减半递归或迭代的层数是log n层。如果笔试考Python要注意**运算符和pow函数的内置实现本身就是快速幂但在实际工程中直接调pow。不过笔试编程题为了展示思路还是建议手写一遍快速幂因为有些阅卷系统会有代码检查疑似调库的解法虽然能过但在面试复盘环节会被追问。位运算也是算法C卷喜欢在编程题里隐藏的考点。比如判断一个数是不是2的整数次幂n 0 且 n (n - 1) 0再比如求一个数二进制中1的个数可以用n n - 1循环。这些技巧不一定单独出题但往往内嵌在动态规划或图论问题中掌握它们能节省大量运行时间。3. 机器学习与深度学习高频考点算法岗位的差异化竞争3.1 K-Means、KNN与BM25这些基础模型的底层逻辑回到算法C卷的机器学习部分。这部分覆盖的范围广但难度偏基础只要系统学过一轮机器学习课程拿分问题不大。我在备考时把高频考点分成两类基础模型类和方法论类。基础模型类里K-Means、KNN、朴素贝叶斯、SVM这几个是绝对重点。K-Means喜欢考的问题有三个一是初始中心点怎么选随机初始化可能导致收敛到局部最优所以有个K-Means策略让初始中心彼此尽量远二是K值怎么确定常用方法是肘部法则三是K-Means的收敛性是局部最优还是全局最优答案是局部最优它是一个EM算法的特例。这三个问题在选择题里经常换着花样出现你只要把握住“K-Means是迭代式局部搜索”这个本质基本都能答对。KNN是另一个几乎必考的点考得最多的就是k值选择的影响k值太小模型复杂容易过拟合决策边界很曲折k值太大模型过于简单可能把不同类别的样本拉在一起产生欠拟合。另外还要知道KNN的三个基本要素k值选择、距离度量、分类决策规则。距离度量默认是欧氏距离但如果特征量纲差异很大比如一个特征范围是0到1另一个是0到10000直接算欧氏距离会让大数值特征主导需要对特征做标准化或归一化。这是工程里最常见的坑笔试也会考。BM25虽然更多出现在搜索、推荐场景但既然它出现在热搜词里说明很多人在准备算法笔试时也遇到了。BM25本质上是TF-IDF的一种改进版它在计算词权重时引入了文档长度归一化和词频饱和函数避免了一个词在长文档里反复出现导致权重虚高的问题。笔试如果考到大概率是问BM25相比TF-IDF的改进点在哪里答“文档长度归一化”和“词频饱和度控制”就能得分。3.2 损失函数、正则化与梯度消失选填题的重灾区模型训练相关的概念是机器学习选择题的重点过拟合的解决手段、梯度消失的原因、L1和L2正则化的区别这些几乎每份算法试卷都会出现。损失函数方面我见过的高频考题是分类问题为什么用交叉熵而不是均方误差因为交叉熵配合Softmax的梯度形式是(y_pred - y_true)而均方误差配合Sigmoid的梯度里含有sigmoid的导数项σ(x)在饱和区域这个导数趋近于0收敛会非常慢。这个考点在很多面经里也有笔试出现概率极高值得重点记。正则化L1和L2的区别也是经典考点L1会让权重稀疏化因为它等价于在优化中加入拉普拉斯先验在零点附近有尖锐的峰值容易把不重要特征的权重压到0L2等价于高斯先验会让权重整体变小但不会变成0。为什么L1更容易产生稀疏解从优化角度理解L1在零点不可导而坐标下降法在更新每个维度时有较大概率直接把该维度置为0。答案记住一句话就好L1提供稀疏性L2提供平滑性。梯度消失和梯度爆炸的问题笔试常考的是“在深层神经网络中使用Sigmoid激活函数容易导致梯度消失为什么”因为Sigmoid导数的最大值只有0.25反向传播时多层连乘会让梯度指数级衰减。解决方案包括使用ReLU等导数恒为1或0的激活函数、Batch Normalization、残差连接等。这些多选题要注意“全选”的情况因为选项确实都可能正确。3.3 PID与卡尔曼滤波控制类算法为何出现在算法卷里你可能觉得奇怪之前的搜索热词里出现了“PID控制算法”和“卡尔曼滤波算法”它们难道不是自动化专业的内容吗怎么会和算法校招笔试扯上关系实际上如果算法C卷偏向具身智能、机器人、自动驾驶方向快手2020年前后也布局过相关领域控制论基础确实会出现在题目里。更何况很多检测算法的笔试本身就会考这些。PID算法最常见的考点是P、I、D三个环节各有什么作用P是比例环节当前误差越大输出越大I是积分环节消除稳态误差D是微分环节抑制超调但会放大噪声。如果一个控制系统稳态误差大优先加积分环节如果超调严重优先加微分环节。这个逻辑在选择题里几乎年年出现背下来就能得分。卡尔曼滤波考得更浅通常只考“它是用来做什么的”以及“它分哪两步”。卡尔曼滤波主要用于从带噪声的传感器观测中估计系统状态属于最优估计算法。它的核心是“预测更新”两个步骤预测阶段利用系统模型推断当前状态和协方差更新阶段结合观测值进行最优融合。笔试如果出了“卡尔曼滤波的两个核心步骤是什么”这样的填空题你不会就说不过去了。我在备考时对这部分的态度是不深究公式推导但把每个算法的“是什么、解决了什么问题、有哪些关键步骤”背熟。因为笔试选择题考的深度就到这个层面而真正让你手推卡尔曼公式的岗位通常会在面试环节单独考察。4. 编程题实战从读题到AC的关键决策点4.1 复杂度估算在动键盘之前就想清楚能不能过编程题是算法C卷里区分度最大的部分。两道题如果全AC笔试通过基本是稳的如果只AC一道就要看选择题的正确率了。我当时的实战经验是看到题目后先不急着写代码先花30秒估算一下数据范围判断该用什么复杂度的算法。大厂笔试系统一般会明确给出数据范围比如n ≤ 10^5这意味着O(n²)大概率超时O(n log n)可以过O(n)最稳。按照“1秒可以执行约10^8次简单操作”这个经验法则10^5的n只能容忍O(n log n)或更优的算法。如果n ≤ 10^3O(n²)基本没问题n ≤ 10^6必须O(n)n ≤ 10^9只能O(log n)甚至O(1)此时大概率是数学技巧题。我见过太多人在笔试中犯的错误一上来就写暴力样例通过了就以为完事大吉结果系统提交后大面积超时。所以我的习惯是先把“最坏情况的循环次数”写在纸上算一遍确认能过再动键盘。如果一个题想不出低复杂度算法也建议先写上暴力版这样至少能拿到部分分数有些笔试系统会按照通过的数据点比例给分。4.2 贪心的失败与DP的兜底经典坑题复盘编程题里需要DP的题目往往是最容易出错的因为DP的难点在状态定义和转移方程而不在代码本身。我记得快手C卷当时有一道题大意是要在一系列任务中选择一组不重叠的任务使得总收益最大。这个题的核心状态定义是dp[i]表示前i个任务能获得的最大收益转移时需要考虑“选第i个任务”和“不选第i个任务”两种情况。很多同学第一反应是贪心按结束时间排序优先选结束时间早的任务。这个思路在“每个任务的收益都相同”的区间调度问题中是正确且最优的但如果每个任务的收益不同贪心就失效了。你必须用DP因为存在“同一时间段内虽然结束时间晚但收益高”的冲突选择。这个“贪心失效”的坑在笔试里很经典值得重点标记。做DP题还有一个容易忽略的点边界条件一定要初始化正确。dp[0]是0还是某个值决定了整个数组的递推走向。我当时做这道题时一开始没处理好dp数组下标和任务编号的对应关系递推结果差了整整一条街后来用了“任务编号从0开始dp长度加1dp[0] 0”这个惯例才理顺。4.3 ACM风格输入输出与边界条件被扣分的隐形杀手算法笔试的输入输出格式不一定都是“核心代码模式”有些试卷会采用类似ACM竞赛的“完整代码模式”需要你自己处理输入输出。这两种模式的区别很大不熟悉完整代码模式的同学容易在这里吃大亏。如果采用ACM风格你要特别注意这几个点第一输入可能有空格和换行混用读取时用while循环判断文件结束不要假设只有一行第二输出格式严格要求比如每个结果占一行最后一个结果后是否要加换行第三把输入读完之前不要提前返回否则会导致部分测试点没读到。Python里用sys.stdin.read()一次性读入再split比逐行input()更稳妥C里用ios::sync_with_stdio(false)和cin.tie(nullptr)加速输入输出已经写进我的默认模板。边界条件的坑比想象中常见。比如求数组中的最大子段和最简单的Kadane算法只需要O(n)但你需要单独考虑全负数的情况——此时最大子段和应该是数组里最大的那个负数而不是0。如果初始化max_sum 0遇到全负数数组就会出错。这种边界问题在样例输入里往往不会暴露只在隐藏测试点里翻车。5. 备考时间线与方法论如何在一个月内达到笔试状态5.1 知识点优先级先保基础分再冲难题如果距离笔试只剩一个月我强烈建议你不要去啃难题偏题而是把时间花在“确定性高”的知识点上。什么是确定性高就是你知道笔试一定会考、且你复习了就能拿分的板块。我把这些板块按优先级排了个序第一梯队是排序算法复杂度与稳定性、KMP的next数组、Dijkstra与堆优化、动态规划基础01背包、最长公共子序列、二叉树遍历特别是层序与各种遍历的相互构建、链表操作反转、环检测。这些是数据结构与算法的基础也是校招笔试题库的“常青树”。第二梯队是机器学习基础中的聚类、KNN、正则化、损失函数、各类优化器SGD、Adam。第三梯队才是冷门知识点比如后缀数组、AC自动机、网络流这些一个月内如果基础不牢可以战略性放弃。我自己的亲测经验是把第一梯队的东西复习扎实至少能拿到笔试70%的分数。剩下的30%靠机器学习基础补15%最后15%看临场发挥。第二梯队和第三梯队的题目往往是用来区分高分的不是用来区分“过线”的。5.2 刷题策略与错题整理用最少时间覆盖最多考点很多同学刷题有一个误区整天在LeetCode上刷中等难度的题刷了300道但还是觉得心里没底。因为笔试不只考LeetCode还考概念题而且概念题的正确率往往决定了你能否过线。我在备考时把每天的复习拆成两部分上午刷2道算法题从高频题单里挑下午用1小时做题本上的机器学习概念题用手机快速翻。刷算法题不要追求题目数量而是追求覆盖考点。每次做完一道题在题号后面标注它考了什么知识点比如“优先队列贪心”“二维DP状态压缩”“快慢指针”一周后你会发现自己的知识版图有很明显的高频区和空白区。空白区如果出现次数多就要专门找几道同类题补齐。错题整理我的方法是“当天错当天整”而且绝不手抄题目只记录“我当时为什么错”和“正确的分析路径”。比如我错的KMP题我会写“错因把next[i]的含义理解成跳转下标正解先判断题目定义的是前缀函数还是失配函数。”这种记录方式在考前回顾时效率极高半小时能过完一个月的所有错题。5.3 笔试现场的实战复盘时间分配与心态管理最后说说笔试现场的实战策略。拿到试卷后不要立刻开始做题先花1到2分钟把整张卷子的题目浏览一遍对题型和题量有个整体把握。我一般会先做编程题再做选择题因为这正好是“先攻坚、后捡分”的顺序。编程题如果卡住了不要死磕超过30分钟先跳过去做选择题回头再补。选择题里遇到不确定的多选题优先用排除法至少排除两个选项后再猜测正确率会高很多。还有一个容易被忽略的点很多笔试系统允许你切换题目并修改之前的答案所以不要怕提交错误先提交一个能过的版本再回头优化。我当时有一道编程题第一版是O(n²)的DP提交后超时后来在还剩20分钟时改成O(n)的双指针解法最终拿到了AC。如果一开始就追求完美可能连第一版的分数都拿不到。心态上我记得有同学在笔试前夜焦虑到失眠结果考场上脑子一片空白。我的建议是笔试前三天不要再学新知识只复习错题本和老知识点考前一晚保证睡眠考试当天提前半小时到电脑前调试好IDE环境准备好自己常用的代码模板比如并查集、最大公约数、输入输出加速这能让你在开考时处于最舒服的状态。结尾一点个人体会快手这套算法C卷回头看看其实就是“基础为王”四个字。它没有刻意出偏题怪题但每一个常规考点背后都有一层“你到底是背的结论还是理解原理”的审视。我在笔试中真正拉开差距的不是哪个冷门算法突然灵光一闪而是选择题里那些机器学习基础概念全部答对编程题用模板快速写完并预留了充足的检查时间。准备2020届秋招时我用了一个月时间做上面这些事最后拿到快手的面试邀约。那段经历让我确信算法岗笔试准备方向对了投入就一定会有回报。如果你现在正在准备快手或同类大厂的算法岗笔试不妨把这份复盘当成一份查漏补缺的清单。看看KMP的next数组能不能手算Dijkstra的贪心正确性能不能一句话讲清楚过拟合和正则化的关系能不能说得流利程序模板是不是准备好了。然后按自己的节奏去刷题、去总结、去模考把笔试前的每一个小时都用在刀刃上。