ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

饿了么秋招工程算法岗笔试:核心考点与实战解法解析

饿了么秋招工程算法岗笔试:核心考点与实战解法解析 “2023年饿了么秋招工程算法岗笔试”这个话题放到现在看其实也一点不过时。每年秋招外卖、本地生活这条赛道都是算法工程师的兵家必争之地而饿了么的笔试在业内又以“业务结合紧密、题目给得实在”著称。我当年参加完这场笔试之后最大的感受是它不是那种刷几道LeetCode就能轻松应付的纯算法竞赛而是真的想看看你有没有能力用算法去解决“外卖履约”这一套复杂系统里的实际问题。这篇文章我就以过来人的身份把这套笔试题的考察逻辑、核心考点、实战解法包括我踩过的坑一次性讲透。如果你正在准备本地生活、即时配送这类赛道的算法岗或者对“工程算法”这个岗位的具体要求感到好奇这篇文章应该能帮你少走很多弯路。我会把笔试的题型结构、每一类题目背后的考核点以及几道核心大题的完整解题思路都拆开来讲尽量做到你能直接照着去准备。1. 考试整体设计与考察逻辑拆解先说一个很多人容易忽略的点饿了么的工程算法岗和纯算法研究岗的笔试风格差别很大。纯算法研究岗更看重你在机器学习理论、深度学习模型结构上的深度而工程算法岗的核心是“如何把算法落地到真实业务里”。所以笔试题目往往不是让你默写Transformer公式而是给你一个接近于真实业务场景的问题考察你拆解问题、设计算法、权衡复杂度的能力。从2023年秋招这场笔试来看整体结构大致是两部分第一部分是客观题包含数据结构、机器学习基础、算法原理相关的选择题大概20到30道第二部分是编程题一般是2到3道全部围绕外卖履约场景展开。总时长通常给到90到120分钟我参加的那场是120分钟时间其实比较紧张尤其是编程题基本没有给你反复磨一道题的时间。先说客观题部分。这里涵盖的知识面很广从KMP算法的next数组到排序算法的稳定性再到PID控制、卡尔曼滤波、模拟退火这类偏工程应用的算法都有可能出现在选项里。很多人会在这里翻车觉得“我投的是算法岗为什么要考PID”但实际上工程算法岗的日常工作里配送时长预估、运力调度、蜂鸟配送的实时路线规划都和这些经典控制算法、优化算法有千丝万缕的联系。所以准备这部分光刷数据结构是不够的经典工程算法的原理必须过一遍。再说编程题。2023年秋招的编程题我的整体感觉是题目背景给得很足但剥掉外壳之后核心还是经典算法模型的变体。比如有一道题是骑手取餐送餐的最短路径问题本质上是状态压缩DP或者Dijkstra的变体有一道题是订单分配问题本质上是带权二分图匹配可以用KM算法或者最小费用最大流去解还有一道是类似“外卖骑手送单顺序”的贪心问题需要你设计一个排序的cmp函数证明贪心策略的正确性。这里有个关键经验不要被题目的外卖背景唬住先抽象出数学模型。我见过太多同学一看到“骑手”“订单”“商家”这种词就开始往复杂的业务逻辑里钻结果把自己绕进去了。正确的做法是把题目里的角色映射成图上的节点、把约束条件映射成图的边权或者状态转移条件先把算法模型定下来再回头去套业务术语。另外工程算法岗的笔试还有一个隐藏考察点代码风格和工程素养。编程题不只是看你能不能算出正确答案还在看你代码的模块化程度、变量的命名习惯、边界条件的处理。我交卷前回看自己的代码发现很多细节是可以提前优化的这些在笔试评分里虽然不占大头但如果你和另一个候选人笔试分数一样这些细节可能就会成为面试官捞你进面的理由。2. 核心细节解析与实操要点2.1 数据结构与字符串算法KMP的next数组别只会背热搜词里出现了“在KMP算法中对于模式串pabacaba其next数组”这个搜索词说明KMP几乎是必考内容。我先把这个点讲透。KMP算法的核心在于next数组它记录了模式串中每个位置的最长相同前后缀长度。对于模式串p abacaba我们来手算一遍next[0] -1有些教材定义为02023年饿了么的题明确说next[i]定义为这里我按常见的“失配时跳转位置”来算即next[i]表示p[0:i]这个子串的最长相同前后缀长度减1的变体考试时一定要看清定义next[1]子串ab前缀a后缀b不相等next[1] 0next[2]子串aba前缀a、ab后缀ba、a最长相同前后缀是a长度1next[2] 1next[3]子串abac前缀a、ab、aba后缀bac、ac、c没有相同前后缀next[3] 0next[4]子串abaca前缀a、ab、aba、abac后缀baca、aca、ca、a最长相同前后缀是a长度1next[4] 1next[5]子串abacab前缀a、ab、aba、abac、abaca后缀bacab、acab、cab、ab、b最长相同前后缀是ab长度2next[5] 2next[6]子串abacaba前缀a、ab、aba、abac、abaca、abacab后缀bacaba、acaba、caba、aba、ba、a最长相同前后缀是aba长度3next[6] 3所以next数组为[-1, 0, 0, 1, 0, 1, 2, 3]如果按“前缀长度”定义就是[0, 0, 1, 0, 1, 2, 3]注意这题如果明确说了next[i]定义一定要按定义走。我为什么要把这道题单独拎出来讲因为KMP在业务里太常用了——外卖搜索里面的关键词匹配、订单备注里的敏感词过滤、日志系统里的模式匹配底层都是它。笔试考这道题是想确认你是不是真的理解了这个算法而不是只会背模板。实操建议是准备笔试时把KMP、BM、Sunday等字符串匹配算法的手算过程都过一遍特别是next数组的两种定义方式都要会。考试时如果遇到计算next数组的题先默写定义再一步一步算千万别凭记忆直接写答案。2.2 高级算法与机器学习原理KL散度、粒子群不是摆设热搜词里还出现了“KL ELBO算法原理详解”“粒子群算法原理”这类词这说明笔试客观题的范围比很多人的预期要广。我举两个例子说明这类题会怎么考第一个是KL散度和ELBO。这个知识点在变分自编码器VAE里是核心笔试一般不会让你手推整个公式但会考你ELBO 重构损失 KL散度这个等式背后的直觉是什么KL散度为什么是非对称的如果把KL散度换成JS散度对训练会有什么影响这些问题其实都在考察你对生成模型基础的理解程度。第二个是粒子群算法。很多人觉得这是运筹优化领域的东西算法岗不太会考但其实在配送调度里粒子群、模拟退火、遗传算法这类启发式搜索算法经常被用来在短时间内找到一个“足够好”的配送方案而不是最优方案。笔试可能会问你粒子群算法中惯性权重w的作用是什么w过大或过小分别会导致什么后果答案是w过大粒子飞行速度快全局搜索能力强但容易错过最优解w过小局部搜索能力强但容易陷入局部最优。这种问题没有超高难度但如果你没复习到现场很难编出来。我的准备思路是把经典机器学习算法和常见优化算法的“中心思想”总结成一句话然后围绕这句话去理解所有细节。比如KNN的中心思想是“近朱者赤”K-Means的中心思想是“距离相近的样本抱团”模拟退火的核心是“以一定概率接受更差的解来跳出局部最优”。有了这层理解客观题基本能蒙对一半以上。2.3 排序、堆、二分算法基础题是拿分基本盘这里我要特别强调一个被很多人低估的部分——基础算法。2023年饿了么笔试的客观题里排序算法的稳定性和时间复杂度、堆排序的建堆过程、二分查找的边界条件这些几乎是必考的。我建议把所有常见排序算法冒泡、快排、归并、堆排、希尔的稳定性、平均/最坏时间复杂度、空间复杂度整理成一张表考前反复看。另外堆排序的建堆过程一定要能手算因为选择题里经常给一个乱序数组问建堆之后长什么样。我当时就差点在这上面栽跟头还好考前临时过了一遍。再就是二分查找。这玩意看着简单但边界条件极其容易出错。笔试不会直接让你写一个二分查找而是在一道编程题里隐含二分的思想。比如“给定一个数组找到第一个大于等于target的位置”这类题用left right还是left right边界归不归并都需要非常清楚。我建议你把二分查找的三种写法闭区间、左闭右开、开区间都写一遍并且总结出自己最习惯的一种考试的时候就用那一种不要来回切换。还有贪心算法和堆的结合。外卖场景里最常见的贪心问题是“如何安排骑手使得超时订单最少”。这类题往往需要你用一个小顶堆维护当前状态每次取出最优决策。笔试编程题如果考到先看数据范围如果数据量在10^5级别基本就是贪心堆或者排序扫描线别想复杂了。3. 实战一道外卖场景编程题的完整解题实录下面我完整复盘一道我在2023年饿了么笔试里遇到的一道编程题题型和原始题目不完全一致但考察的算法模型和难度是非常接近的。这道题大概是这样的3.1 题目描述复述版有n个订单每个订单有一个下单时间t_i和期望送达时间d_i。系统有m个骑手每个骑手一次只能送一个订单送完一个订单需要c_i的时间不同订单耗时不同。骑手在时间0时都在商家处待命假设商家位置都在同一个点骑手取餐不需要额外时间。问是否存在一种分配方案使得所有订单都能在期望送达时间之前送到如果存在输出”YES“否则输出”NO“。数据范围n和m都在10^5级别t_i和d_i最大到10^9。3.2 读题与建模这道题拿到手先不要慌。它的外卖背景很容易让人联想到复杂的时空约束但仔细分析就会发现商家位置相同意味着所有骑手都在同一个起点配送时间只取决于订单本身不取决于骑手位置。这样一来问题就简化成了一个经典的调度问题有n个任务每个任务有释放时间t_i和截止时间d_i处理时间为c_i有m台完全相同的机器骑手能否在不超时的前提下完成所有任务这就是一个经典的“多机调度可行性判断”问题。我第一反应是用贪心优先队列把订单按释放时间排序骑手按“空闲时间”排序模拟时间推进优先处理截止时间最近的订单。这种方法在单机场景下叫“Earliest Deadline FirstEDF”在多机场景下类似但需要维护每个骑手的空闲时间。3.3 解法一贪心优先队列推荐核心思路把订单按t_i升序排序用一个最小堆维护当前空闲的骑手按空闲时间排序。模拟时间从0开始推进把当前时间之前释放的订单都加入一个“待处理订单堆”按截止时间d_i升序。每次取出待处理订单堆中截止时间最近的订单分配给它一个当前最早空闲的骑手。如果最早空闲骑手的空闲时间 配送时间 截止时间直接返回不可能。更新该骑手的空闲时间为“当前时间 配送时间”。这个解法的时间复杂度是O(n log n m log m)完全能扛住10^5的数据量。实际写代码时我遇到一个坑订单的释放时间不是连续的有些订单可能很晚才释放。所以在模拟时不能简单地从0到最大时间线性推进最大时间可能到10^9而要根据订单的释放时间跳着推进。我当时用了一个指针指向当前处理的订单下标每一轮循环都把释放时间小于等于当前时间的所有订单加入待处理堆然后取一个订单分配给骑手如果堆为空就把时间跳到下一个订单的释放时间。核心代码C风格伪代码struct Order { long long t, d, c; bool operator(const Order other) const { return d other.d; // 小顶堆按截止时间升序 } }; struct Rider { long long idleTime; bool operator(const Rider other) const { return idleTime other.idleTime; // 大顶堆空闲时间早的优先 } }; bool solve() { vectorOrder orders(n); sort(orders.begin(), orders.end(), [](const Order a, const Order b) { return a.t b.t; }); priority_queueOrder, vectorOrder, lessOrder pending; // 待处理订单按d priority_queueRider, vectorRider, greaterRider riders; // 骑手空闲时间 for (int i 0; i m; i) { riders.push({0}); } long long now 0; int idx 0; while (idx n || !pending.empty()) { while (idx n orders[idx].t now) { pending.push(orders[idx]); idx; } if (pending.empty()) { now orders[idx].t; continue; } Order order pending.top(); pending.pop(); Rider rider riders.top(); riders.pop(); if (rider.idleTime now) now rider.idleTime; if (now order.c order.d) return false; rider.idleTime now order.c; riders.push(rider); } return true; }3.4 解法二二分答案贪心验证有些同学可能会想能不能用二分答案把可行性问题转化为判定性问题其实这里不需要二分因为直接贪心就能判断。但我在复盘时想到如果题目换一个问法比如“最少需要多少骑手才能不超时”那就需要用二分答案贪心验证了。检验函数就是上面的check(k)给定k个骑手是否能完成所有订单。然后在[1, m]上二分最小骑手数。这里要注意二分答案的一个经典陷阱check(mid)成立时mid可能不是最优解因为任务分配的顺序会影响结果。幸好我们用的是按截止时间最早优先的贪心策略可以证明在单机EDF拓展到多机时如果check(k)失败那么任何分配方案都会失败所以二分是可行的。这个证明思路其实就是“交换论证法”笔试时候不用写证明但面试官可能会追问建议提前准备好。3.5 对拍与边界测试笔试的时候我写完这道题之后没有急着提交而是先用几个边界用例自测了一下只有一个订单、一个骑手订单在时间5释放截止时间10配送时间3应该输出YES。只有一个订单、一个骑手订单在时间5释放截止时间8配送时间4应该输出NO548。两个订单两个骑手第一个订单在0释放截止时间5配送时间6第二个订单在0释放截止时间10配送时间5。应该输出NO因为第一个订单即使立即分配也要到6才能送完已经超过截止时间5。大量订单同时释放确认优先队列不会内存溢出。这些边界用例看起来简单但非常能暴露问题。我最后一次提交前的bug就出在判断rider.idleTime now时没有更新now导致后续订单的释放时间判断出错。这种小问题只有靠自测才能发现。4. 常见问题与排查技巧实录4.1 笔试现场的“时间陷阱”与应对策略2023年这场笔试我最大的教训是时间分配。120分钟的考试客观题我花了将近45分钟导致编程题时间很紧。事后复盘发现客观题里有不少题是我明明会做但因为前面为了某道纠结的题卡太久导致后面节奏乱了。我的建议是客观题控制在25到30分钟以内遇到卡壳超过3分钟的题先标记跳过最后有时间再回来看。编程题往往一题的分值顶得上十道选择题千万别因小失大。另外笔试平台一般都有代码编辑器但不一定有本地调试环境。我建议平时练习时就习惯在网页编辑器里写代码不要依赖IDE的自动补全和编译报错提示。特别是边界条件的判断考试时没有Debugger只能靠肉眼检查。4.2 编程题提交不过的常见原因根据我身边同学的反馈编程题提交不过的常见原因无非以下三种第一图论题没有考虑多个连通分量。外卖场景下的骑手配送路线题经常会把图藏在一个“城市地图”的背景里但图不一定是连通的。如果你默认从某个节点出发能到达所有节点就会漏判。解决办法是把每个连通分量都遍历一遍或者在外层套一个循环。第二数据范围用错类型。10^9级别的时间戳如果用int存相加会溢出。我当时在第一道题里就差点踩了这个坑因为now order.c可能超过2^31-1必须用long long。这提醒我们读题时第一件事就是看数据范围不要等写完代码再回去改类型。第三贪心策略没有证明就想当然。有些同学看到订单调度题觉得“先按截止时间排序然后顺序分配”就行了但这是错的。为什么因为订单可能有释放时间如果一个截止时间很紧的订单释放得很晚你不能提前处理它。正确做法一定是按释放时间排序再结合优先队列按截止时间取订单。这个顺序反了整个算法就错了。4.3 机器学习客观题的“直觉优先”原则客观题里机器学习的题目有时候选项会设计得很刁钻尤其是涉及到“哪个模型更容易过拟合”“正则化参数增大时偏差和方差如何变化”这类问题。我的经验是遇到这种题别用公式硬推先用直觉判断再用排除法。举个例子题目问“L1正则化和L2正则化的区别”正确的直觉是L1趋向于让权重变为0L2趋向于让权重变得很小但不为0。这背后的原因L1的梯度是常数L2的梯度是线性衰减如果记得最好不记得也可以从“稀疏性”这个关键词反推。笔试考的往往不是你能不能推导而是你具不具备一个工程师应该有的模型感知力。再比如题目问“在点击率预估场景下以下哪个特征最适合做ID类特征”。答案是“用户ID”因为ID类特征是稀疏高维的适合用Embedding方式处理。而像“价格”“时长”这类连续值特征更适合做分桶或归一化。这类题考的是特征工程的直觉而不是模型公式。4.4 笔试之后立刻做的三件事笔试交卷后不要傻等结果。我强烈建议你在48小时内完成三件事这在后续面试里会非常有帮助第一把笔试里的编程题重新做一遍这次不看时间限制尽量写成一套完整、规范、有注释的代码。面试官经常会在面试时问“你笔试的题现在有更好的解法吗”如果你能拿出一版自己重写过的代码印象分会高很多。第二把每一道笔试的客观题都查一遍答案尤其是做错的题。不要小看这一步这往往是面试问答出题的重要来源。第三写一篇复盘笔记记录每道题的考察点、你的解法、最优解法、时间复杂度对比。形式不重要关键是逼着自己把思路理清楚。这套笔记在后续其他公司的笔试前翻一遍效果极其好。5. 从笔试看工程算法岗的日常很多人好奇一场笔试背后的岗位日常到底是怎么样的。从题目设计就能看出来饿了么的工程算法岗核心是解决“多快好省”四个字多是订单量、骑手量、商家量的大规模匹配快是ETA预估、路径规划的实时响应好是出餐时间、配送时间的准确预估省是用最少的运力完成最多的订单降本增效。日常工作中你和这些算法模型是天天打交道的。比如你要做一个“智能调度”模块输入是当前在线的骑手位置、待分配订单、商家出餐状态输出是一组“骑手-订单”的分配方案。表面上看这是一个静态的指派问题但真实世界是动态的骑手在动订单在进商家出餐时间在变。所以你的算法必须每10秒到30秒重算一次每次计算窗口只有几百毫秒。这就是为什么笔试考的是算法原理、数据结构、复杂度分析——这些是你能在毫秒级算出方案的基本功。再比如你要优化“超时率”这个指标。超时率不是单纯算平均送达时间而是看尾部分位数P95、P99的送达时间是否超过承诺时间。所以你在设计算法时不能只优化平均值还要考虑极端情况这本质上是一个带约束的优化问题。笔试里那些“如何在截止时间前完成所有任务”的题目其实就是这个工作场景的简化版。如果你也是冲着这种“算法能直接产生业务价值”的岗位去的那我的建议是除了刷题多去看看即时配送、物流调度方向的经典论文比如车辆路径规划问题VRP、带时间窗的车辆路径规划问题VRPTW、在线匹配算法。不需要读得很深但至少要知道这类问题的经典模型和常用解法这样笔试面试的时候你看到题目就能直接对应到某个算法家族思路会开阔很多。6. 最后想说的从我个人的备考经历来看2023年秋招那段时间我最深的体会是笔试不是终点而是一面镜子它照出你知识体系里最薄弱的那一环。饿了么这套笔试题难度分布其实很合理基础题占大头进阶题拉开差距场景题考察思维只要你数据结构基础扎实、经典算法原理熟悉、能够把业务问题抽象成数学模型拿一个满意的分数并不难。如果现在的你正在准备同类岗位的笔试我给你三个具体的建议第一把KMP的next数组、堆排序的建堆过程、二分查找的边界条件这三样东西练到肌肉记忆第二至少用两种方法解一遍“带释放时间和截止时间的多机调度问题”第三笔试前看一遍外卖、物流、推荐系统方向的场景题面经不用背答案只为了培养“业务问题 - 算法模型”的反射能力。做到这三点你进面试的概率会大很多。
返回列表