饿了么秋招算法岗笔试复盘:从KMP到动态规划的业务型考察

发布时间:2026/9/1 15:23:13
饿了么秋招算法岗笔试复盘:从KMP到动态规划的业务型考察 2024年秋招的算法岗笔试我是在一个周日下午做的。当时刚投完简历不到一周收到饿了么第二批笔试通知的时候说实话有点没底——网上关于第一批笔试的讨论已经有不少了但第二批的题型、难度、考点似乎很少有人系统地复盘过。真正做完之后我发现这场笔试和大厂通用算法题海战术的路子不太一样它更偏向“算法基本功 业务理解 工程细节”的组合考察。这篇文章我就把整个流程完整复盘一遍从题型分布、选择题考点、编程题实战到考后的备战调整都会说到尤其适合正在准备外卖、本地生活类业务算法岗的同学参考。开头我先说结论这批笔试整体难度属于中等偏上和纯互联网大厂通用笔试比它更重视候选人对算法原理的理解深度而不是单纯刷题数量。整个笔试大致由选择题、编程题和一道偏场景的简答题组成时长接近两个小时很多人挂在时间分配上——前面选择题太纠结后面编程题没时间调。第二个大坑是编程题的边界条件用例看起来简单但隐藏的全是极限数据。这篇文章我会把每一类题型的考察逻辑和我的实际作答思路给你拆开细讲。1. 这场笔试的实际构成题型、题量与时间分配1.1 题型分布比想象中更“综合”我拿到的这套卷子一共可以分为三块。第一块是选择题大约有十五到二十道分布在数据结构、算法、机器学习、深度学习和少量数学基础上。这里面的数据结构题很典型比如字符串匹配、排序稳定性、堆调整、链表操作等机器学习部分则集中在分类模型、聚类、评估指标这些基础概念上。深度学习考了激活函数、梯度消失、正则化手段难度不算高但需要真正理解原理靠背结论容易踩坑。第二块是编程题一共三道难度梯度比较明显。第一道偏简单基本是排序加贪心的组合用来热身第二道是中等偏上的动态规划状态定义和转移方程想清楚之后代码量并不大但空间优化是个关键点第三道更偏向实际场景建模会结合调度或路径规划类背景考察的其实是图论或搜索算法的应用能力。整体来看编程题并不是“背模板就能过”的题型每道题都需要先读懂业务描述再抽象成算法模型。第三块是场景简答题。这个部分比较有意思它不是让你写代码而是给一个业务场景比如外卖配送的订单分配、骑手路径规划、搜索排序中的相关性优化让你从算法方案的角度去分析。这个环节很考验业务理解光会刷题是不够的。1.2 时间分配我先吃了“前松后紧”的亏我实际做的过程中选择题花的时间比预期多了将近十分钟。原因是有一道KMP next数组的题我虽然复习过但题目里给出的字符串比较长还要求手算next数组的每一项我一边算一边担心出错反复核对了好几遍。到编程题的时候时间只剩一个半小时不到第二道DP题因为状态定义迟迟没想清楚又耗了不少时间。复盘之后我给自己定了一个时间分配原则选择题平均每道不超过两分钟遇到拿不准的先标记全部做完后再回头处理编程题按“1 : 2 : 2.5”的比例分配时间先保证第一道一次通过再集中火力攻第二道第三道至少写出可运行的暴力版本拿到部分分数。场景题放在最后二十分钟处理重点是把方案框架写清楚画清楚步骤而不是追求篇幅。如果你之后要参加类似笔试我建议先花两三分钟快速浏览全套题目看看编程题都是什么难度再决定做题顺序。我看完卷子后调整了顺序先做了最熟悉的一道题心态稳定之后再去啃难题整体节奏会舒服很多。2. 选择题里的算法基本功KMP、排序、图论与快速幂的考察方式2.1 KMP的next数组常考、易错、看上去送分实则暗坑热搜词里有“在kmp算法中对于模式串p“abacaba”其next数组定义为……”我拿到的那套卷子里虽然没有完全一样的题但确实有一道KMP相关的选择要求计算一个模式串的next数组值。很多同学对这个知识点的印象停留在“背代码”一旦要手动计算就容易出问题。这里我说一下我的计算套路。KMP的next数组通常定义为对于模式串Pnext[i]表示P[0...i-1]这个子串中最长的相等真前缀和真后缀的长度。这个“真”字很关键也就是说前缀和后缀都不能等于整个子串本身。比如P“abacaba”我们从i0开始逐项算i0时约定next[0] -1i1时子串是“a”真前缀和真后缀都为空所以next[1]0i2时子串是“ab”没有相等的真前缀和真后缀next[2]0i3时子串是“aba”最长相等真前缀和真后缀是“a”长度1next[3]1i4时子串是“abac”没有相等next[4]0i5时子串是“abaca”最长相等是“a”next[5]1i6时子串是“abacab”最长相等是“ab”next[6]2i7时子串是“abacaba”最长相等是“aba”长度3next[7]3。如果考试时遇到这类题我建议不要跳步一个字一个字地写出每个子串再判断前缀后缀。因为题目很容易给你一个看似正常但中间某项算错的选项用来筛选粗心的考生。KMP这个知识点之所以被频繁考察是因为它体现了“通过预处理避免重复匹配”的算法思想而这个思想在很多实际场景中都能延伸比如BFPRT、AC自动机等。笔试考察它一来是看基础扎不扎实二来是看你能不能从“背模板”上升到“理解原理”。2.2 排序、堆排序与TopK不只是比复杂度另一道选择题问的是排序算法稳定性问题给了几个排序算法让选出“不稳定”的那些。这种题看起来简单但特别容易失分因为很多同学只会背结论不理解为什么。我当时是这样记的选择排序、快速排序、堆排序、希尔排序是不稳定的插入排序、冒泡排序、归并排序、基数排序是稳定的。关键要理解不稳定的根源。以选择排序为例它每次选择最小元素和当前位置交换这个交换操作可能把相同元素的相对顺序打乱。快速排序则是因为有跨距离的交换比如主元交换可能把后面的相同元素甩到前面。堆排序就更直接建堆和调整堆时数组元素会大幅度移动稳定性无从谈起。堆排序本身在笔试里经常和TopK问题绑定考察。比如给一个很大的数据流要求找出最大的K个数最优解法就是用容量为K的小顶堆遍历数据时如果当前元素比堆顶大就替换堆顶并堆化。选择题可能会问时间复杂度是O(n log K)空间复杂度是O(K)。这里我特别想说笔试中很多问题表面考排序实际上考的是“你能否根据场景选择合适的排序算法”而不是单纯的八股。2.3 贪心、Dijkstra和快速幂经典算法为什么值得反复考贪心算法在笔试题里出现频率极高。我遇到的选择题大多不是让直接写代码而是给一个场景让判断使用贪心策略是否可行或者要求说明贪心失效的情况。比如经典的“区间调度”问题按结束时间排序就是正确的贪心选择但换成“区间选点”问题策略就要变成按右端点排序后每次选最右的点。这类题考察的是你能否快速判断一个优化问题是否具备贪心选择性质。Dijkstra算法也是一道经典选择常客。最常见的问题有两个一是为什么Dijkstra不能处理负权边答案是它基于“当前距离最短的节点不会再被更新”的前提如果有负权边已经确定最短路的节点可能被后续节点更新算法会失效二是用堆优化后的时间复杂度是多少答案通常是O((VE)logV)。如果你能理解Dijkstra实质上是“贪心思想 动态规划”的结合这类题就不容易出错。快速幂是另一道几乎必考的基础算法。选择题经常直接问计算x^64需要多少次乘法答案是6次因为指数可以二进制拆分成642^6每次平方即可。如果指数不能拆成完全二次幂比如x^30则需要拆成30的二进制11010对应2^42^32^1结合平方和乘法一共需要log n级别的次数。快速幂虽然代码短但考察点很集中二进制思想、取模边界、防止溢出。我写过一次快速幂的Python实现核心就是把指数不断右移底数不断平方def fast_pow(base, exp, mod): result 1 base % mod while exp 0: if exp 1: result (result * base) % mod base (base * base) % mod exp 1 return result你要是准备笔试这种题一定要能默写并且要理解每一行的作用因为选择题里经常出现“漏了取模”“边界为0”之类的变体。3. 机器学习、深度学习与业务场景这轮笔试的“软实力”分层3.1 机器学习基础题目KL散度、聚类、KNN这些概念不能只靠背这一部分的选择题比纯数据结构题更能拉开差距。我印象比较深的一道题是关于K-Means聚类和KNN分类的区别。选项里有好几个容易混淆的说法比如“K-Means是监督学习KNN是无监督学习”“K-Means是无监督学习KNN是监督学习”“两者都需要训练过程”等等。正确答案是K-Means是无监督聚类KNN是监督分类KNN的训练过程实际上是存储训练样本推理时才计算距离。另一道题涉及KL散度因为热搜词里也出现了“kl elbo算法原理详解”。KL散度本质上衡量两个概率分布的差异它是非负的但不满足对称性。选择题可能给你四个式子让判断哪个是KL散度的正确定义。这时候你得记得 D_KL(P||Q) Σ P(x) log(P(x)/Q(x))而不是相反。如果题目升级还可能会问到KL散度在变分推断中的地位也就是ELBO的推导。我复习的时候把这一块当成重点因为很多业务场景题都会用概率模型去描述用户行为。除了上面这些评估指标也很常考。比如精确率和召回率的公式F1分数的调和平均数定义AUC的含义。有一道题给了一个混淆矩阵让计算精确率很多人会把精确率和准确率搞混。你要记住精确率是预测为正中真正为正的比例召回率是实际为正中被预测为正的比例。3.2 深度学习基础激活函数、梯度消失和正则化深度学习的选择题难度不大但覆盖面广。我遇到的有Sigmoid函数在深层网络中的梯度消失原因、ReLU在负区间的梯度问题、Dropout的训练和推理阶段行为差异、BN层的计算流程等。举个例子问为什么深层网络用Sigmoid容易梯度消失。答案要点有两个Sigmoid导数最大值是0.25连乘多个小于1的数会让梯度指数级衰减另外Sigmoid输出不是零均值的会导致上层神经元输入始终为正进而产生 zigzag 式的梯度更新。这种题需要你不仅能写出公式还要能把“链式法则连乘导致梯度消失”的直觉说清楚。关于Dropout很多同学会漏掉一个关键点训练时随机丢弃神经元推理时为了保证输出期望一致需要对权重乘以保留概率或者把输出除以保留概率。选择题如果问“模型上线推理时是否应该dropout”答案当然是不需要。这个知识点简单但很能反映候选人是否真的动手训练过模型。3.3 业务场景里的算法BM25、PID、卡尔曼滤波、图像增强这些词为何被提及搜热词里出现了一堆看起来不相关的算法名词比如“bm25算法”“pid算法”“卡尔曼滤波算法”“图像锐化的拉普拉斯算法”。这些词出现在同一批搜索里其实说明了算法岗笔试的一种倾向不局限于传统机器学习还会结合具体业务场景考察你对常用算法工具的理解。以饿了么这类本地生活平台为例搜索排序里会用到BM25或类似的相关性打分算法它本质上是词频和逆文档频率的扩展用来衡量文档和查询之间的相关性。选择题可能让你比较BM25和TF-IDF的差异比如BM25引入了文档长度归一化和词频饱和机制避免长文档因包含更多关键词而“天然占便宜”。PID控制和卡尔曼滤波则更多出现在调度、定位、无人配送等场景。PID算法的核心是比例、积分、微分三个环节的协同比例项负责快速响应积分项消除稳态误差微分项抑制超调。题目可能会问当系统出现稳态误差时应该增加哪个环节的作用答案是积分项。卡尔曼滤波则是一套在噪声环境中做状态估计的方法它通过预测和更新两个步骤融合传感器数据选择题常见的是问它适用于什么场景比如GPS定位、机器人导航。这类题目虽然不让你手推公式但需要你理解算法要解决的问题以及它和传统滤波的本质区别。图像算法也是一类常出现的跨界题目。比如“图像锐化的拉普拉斯算法”和“sobel算法”它们一个是二阶微分算子一个是一阶微分算子。选择题可能会给你一个3x3的卷积核让你判断它属于哪种操作。Sobel用于边缘检测拉普拉斯算子可以对图像做锐化核心思路是提取高频分量再把高频部分叠加回原图。我复习的时候把这些图像算子当作“算法视野扩展”来对待虽然不一定会被问到但遇到了也不至于完全懵。4. 编程题实战复盘从读题、建模到AC的完整链路4.1 第一道编程题排序加贪心真正考察的是审题我拿到的第一道题大意是给定若干任务的截止时间和收益每个任务耗时单位1求能获得的最大收益。看到这题的第一反应就是贪心加排序但具体怎么排是按下线时间排还是按收益排需要好好判断。我的解法是这样先把所有任务按截止时间从小到大排序然后维护一个当前已选任务集合用一个小顶堆保存已选任务的收益。遍历每个任务时先把当前任务加入如果已选任务数量大于当前任务的截止时间说明安排不下了就从堆中弹出收益最小的那个任务。这样做能保证在满足每个任务截止时间的前提下总收益最大。这个思路的核心是每个单位时间安排一个任务截止时间越早的任务越应该优先考虑当出现冲突时舍弃收益最低的任务。写代码的时候我用了Python的heapq代码量不大import heapq def max_profit(tasks): tasks.sort(keylambda x: x[0]) heap [] total 0 for deadline, profit in tasks: heapq.heappush(heap, profit) total profit if len(heap) deadline: total - heapq.heappop(heap) return total这题本身不算难但我看到不少同学容易把排序关键字搞反或者遇到“截止时间从0开始还是从1开始”的问题。笔试里的边界条件往往就在这里题目如果写明任务从时间1开始执行那“len(heap) deadline”这个判断逻辑需要微调直接在代码里写错一个符号用例就过不了。4.2 第二道编程题动态规划的状态定义与空间优化第二道题是一道二维DP题背景描述是网格路径相关的调度问题。大致是给一个网格某些格子有障碍物机器人从左上角走到右下角有多少种不同走法。如果仅仅是这样那是一道很经典的DP题但题目加了一个条件在路径中经过某些特殊格子可以获得额外积分但同一个格子不能重复计分。这类题的重心在于状态定义。我定义dp[i][j]为到达当前位置的路径数如果(i,j)是障碍物dp[i][j]0否则dp[i][j]dp[i-1][j]dp[i][j-1]。如果只是这个公式那太基础了真正的关键是题目要求了“不能重复计分”所以路径是否经过同一个格子不能简单用加法。这时候要意识到如果不允许重复经过那这个题其实变复杂了不是普通二维DP能解决的。我在考场上意识到题目描述里有一个隐含条件我重新读了一遍发现其实只是“同一个格子的积分不能重复计算”并不要求路径本身不重复经过同一个格子。这样一来其实就不需要担心路径重叠的问题只需要在状态转移时考虑当前格子是否走过。最后我选择用三维DP把经过特殊格子的状态压缩成一个bitmask存进状态里走了哪些关键点用位运算维护然后进行状态转移。代码如下from functools import lru_cache def unique_paths_with_score(grid, k): m, n len(grid), len(grid[0]) key_points {} idx 0 for i in range(m): for j in range(n): if grid[i][j] *: key_points[(i, j)] idx idx 1 lru_cache(None) def dfs(x, y, mask): if not (0 x m and 0 y n): return 0 if grid[x][y] #: return 0 new_mask mask if (x, y) in key_points: new_mask mask | (1 key_points[(x, y)]) if x m - 1 and y n - 1: return 1 return dfs(x 1, y, new_mask) dfs(x, y 1, new_mask) return dfs(0, 0, 0)这个解法能保证每个状态都被记忆化但空间复杂度会比较高因为mask是2^k级别k是关键点个数。如果k超过15就会超时。考场上时间有限我把暴力DFS和记忆化版本都写了跑通小数据后就去检查其他题目了。如果你基础不错我更推荐用状态压缩DP来解先 dp[x][y][mask] 表示到达(x,y)且已经访问关键点集合是mask的路径数再用递推。这样复杂度更可控。4.3 从TLE到AC边界条件、输入输出和复杂度的三个检查点笔试最容易翻车的地方不是不会做而是会做但没办法在限定时间内跑通。我总结了自己在实战中反复遇到的三类问题。第一类是输入输出格式。有些平台输入数据量很大用input()一行行读会特别慢Python需要改用sys.stdin.read()或sys.stdin.buffer.read()。笔试时如果发现超时第一反应就应该是检查输入输出而不是去优化算法。我自己的习惯是在开头就写好读取模板避免中途改。第二类是整数溢出问题。Python不存在真正的整数溢出但如果是C或Java那就需要特别小心。即使是Python也会遇到递归层数过多导致栈溢出的情况这时需要把递归改成循环或者用sys.setrecursionlimit()提高递归深度限制。第三类是时间复杂度估算。笔试机器通常能让1秒跑大约10^8次简单运算。如果你写的算法时间复杂度是O(n^2)而n是10^5那基本必挂。我的做法是拿到题先估算n的规模据此判断该用O(n log n)还是O(n^2)的算法。这些经验看起来琐碎但真的能在关键时刻救你一命。我这次第二道题就是靠先跑通暴力版本再逐步优化最后通过了大半测试用例。5. 考后的复盘我重新调整的算法岗备战策略5.1 为什么“刷题量”不等于“笔试通过率”考完之后我认真复盘了一下自己的准备过程和实际表现的差距。刷题量确实有用但它解决的是“见过类似套路”的问题解决不了“理解和迁移”的问题。饿了么这批笔试给我的感觉是题目不是从题库里随便抽的而是有意识地考察“你在面对一个实际业务问题时能不能把它抽象成正确的算法模型”。比如编程题里的路径规划问题本质上考的是图搜索和状态压缩但它披着一个“外卖配送”的场景外套。如果你平时只刷纯LeetCode题看到这种描述会有点慌因为你需要先剥掉业务外壳才能看到核心算法。我后来备战策略里增加了一个专项每天找一道带业务场景的算法题先自己描述一遍“这个场景让我抽象成什么模型”再动手写代码。这个习惯帮助我在后续的其他笔试中明显更稳。5.2 针对业务型算法岗的准备清单我给自己列了一张准备清单分成了四个维度数据结构与经典算法二叉树、链表、栈队列、堆、KMP、快速幂、Dijkstra、拓扑排序等每天选一两类做深度复盘而不是盲目刷新题。机器学习与深度学习基础重点过一遍聚类、KNN、逻辑回归、决策树、SVM的基础原理以及评估指标、正则化、过拟合、激活函数、梯度消失等概念。业务场景建模搜索排序、推荐策略、路径调度、订单分配、定价补贴等多想想这些场景对应的算法方案比如排序里可以用BM25或Learning to Rank调度里可以用贪心、动态规划或启发式搜索。工程细节输入输出优化、空间压缩、边界条件、数值精度、随机种子等每一道题写完代码后都自查一遍这些点。清单上的每项都不要太宏大重点是可执行。我每天大概投入三到四个小时专门做这些训练持续了两周后明显感觉做题时的思路比以前清晰很多。5.3 关于笔试之外的“隐藏信号”我个人觉得这类笔试真正想考察的并不只是你会不会做题。它会通过题目的业务背景观察你是否对目标公司的业务模式有基本了解。比如外卖平台的算法岗如果候选人只知道通用机器学习知识却完全不了解“骑手路径规划”“订单分配”“出餐时间预测”等场景那即使笔试分数不低后续面试也容易露馅。所以我的建议是在准备笔试的同时花一点时间了解目标公司的业务线和算法应用场景。不需要太深但至少要知道他们有哪些核心业务每个业务下可能存在哪些算法问题。这些信息不仅对笔试有帮助对面试时的行为面和业务面更是加分项。回到我自己的这次经历虽然第二批笔试已经结束了但复盘的过程让我真正看清了自己的短板代码熟练度不差差的是在有限时间内快速建模的能力。如果你也在准备类似岗位我希望这篇文章能帮你少走一些弯路。笔试刷题重要但更重要的是把每一道做过的题都吃透真正做到“换一个业务场景我依然认得你”。