前两天整理旧电脑里的面试备份翻出一份2017年美团秋招算法工程师A的笔试回忆题单。那阵子算法岗笔试还远没有现在卷出天际但美团的题目质量在当年一线互联网公司里相当能打——不偏不怪考察面集中在算法与数据结构、机器学习基础、业务建模这三块非常适合用来检验算法工程师的基本功。这篇文章我就把这份题单完整复盘一遍每道题考什么、解题思路怎么走、有哪些容易被忽略的坑再聊聊备考这类笔试时值得注意的地方。不管你是正在准备算法岗校招还是想用一套高质量题目做阶段性自测这份复盘都有参考价值。提醒一下这份题单来自我当年整理后的回忆版部分题目描述经过了补全和改编和原卷不完全一致但考察的知识点和难度曲线是忠实还原的。美团这种大体量公司出题有个特点不会刻意用偏题怪题卡人但会把基础概念挖得很深稍不注意就会在“你以为自己会”的地方翻车。1. 整套笔试题型分布与能力考察逻辑1.1 笔试整体结构与分值权重就我回忆2017年美团算法工程师A这套卷子大致是三种题型混合单场笔试时长在90到120分钟之间题量和总分我记得不太清了但题型结构印象很深。题型大致题量覆盖方向难度感受客观选择题25题左右数据结构、概率统计、机器学习基础中等偏易但陷阱多编程题2到3题经典算法实现、复杂场景建模中等偏难区分度大综合问答题1到2题业务场景中的算法方案设计开放性强考察表达能力选择题里数据结构占比最高树、图、链表、排序都出现过紧接着是机器学习基础概念题。编程题反而是比较朴素的算法题不会涉及特别偏门的数据结构但会把边界条件和复杂度抠得很细。综合题则是给一个外卖或本地生活相关的业务场景让你给出算法方案这也是美团最喜欢考的环节。从分值分布来看客观题是基础分编程题是拉开差距的关键综合题则用来判断你有没有“把算法落地到业务里”的意识。很多同学只刷编程题忽略综合题的准备这其实是个很大的策略失误。1.2 美团出题风格为什么值得反复做有些人看到年份是2017就觉得这套题过时了。我反而觉得这正是它值得认真做的原因。2017年前后是算法岗笔试从“偏重传统算法”转向“机器学习与业务结合”的过渡期。美团这套题恰好站在那个拐点上既有KMP、快排、Dijkstra这类经典算法题又开始大量出现逻辑回归、K-Means、样本不平衡等机器学习问题。这种“两条腿走路”的考察方式对于当下算法岗的笔试准备依然有很强的参考意义。现在很多公司的算法笔试题反而走极端要么全是LeetCode风格要么全是机器学习理论题很少像这套卷子一样兼顾基础功底和业务直觉。另一个原因是美团这套题的所有场景都围绕本地生活服务展开比如配送调度、商家排序、异常订单检测。这类问题至今仍然是算法工程师面试中的高频场景提前通过这套题训练业务建模能力相当于提前储备了核心经验。2. 算法与数据结构真题详解这些题年年都在考2.1 KMP算法next数组计算的两种定义千万别混淆这套题里有一道非常经典的KMP题题目大意是给定模式串 P abacaba要求计算其 next 数组。这道题本身不难但有一个特别容易踩的坑不同教材对 next 数组的定义不统一。很多人背了一套模板就直接套用结果题目给的定义和你背的不一致整道题全错。我当年就栽在这个上面。当时背的是某本教材的“失配跳转”定义next[j] 表示当模式串第 j 位失配时下一次应该用第 next[j] 位继续比较并且规定 next[0] -1。但题目里给的定义是“前缀函数式”的next[i] 表示 P[0...i] 的最长相等真前后缀长度且 next[0] 0。两种定义算出来的数组完全不同。按前缀函数定义来算 P abacaba过程是这样的子串最长相等真前后缀前缀函数值a无长度不能等于自身0ab无0abaa1abac无0abacaa1abacabab2abacabaaba3所以前缀函数式 next 数组是 [0, 0, 1, 0, 1, 2, 3]。而失配跳转式通常定义 next[0] -1后续值等于“当前位置之前的子串的前缀函数值”再经过优化结果完全不同。这提醒我们一个铁律凡是遇到数组定义不统一的题先花10秒确认题目到底用的哪套定义再动手写。这类题要过关不能只背模板。我建议把KMP的两种next定义都亲手推导一遍并且理解为什么失配跳转式要有个 -1 作为哨兵。理解了本质考场上随便题目怎么给定义你都不会慌。附一个前缀函数式KMP的Python参考实现def prefix_function(p): n len(p) pi [0] * n for i in range(1, n): j pi[i - 1] while j 0 and p[i] ! p[j]: j pi[j - 1] if p[i] p[j]: j 1 pi[i] j return pi def kmp_search(text, pattern): pi prefix_function(pattern) j 0 res [] for i, ch in enumerate(text): while j 0 and ch ! pattern[j]: j pi[j - 1] if ch pattern[j]: j 1 if j len(pattern): res.append(i - j 1) j pi[j - 1] return res2.2 快速幂与模运算看似送分实则暗藏精度坑另一道编程题是快速幂。题目通常不会直接说“请你实现快速幂”而是包装成一个场景“求 2^n mod 1000000007”。如果你图省事直接循环乘法n一大就会超时如果没用 long long中间乘法还会溢出。快速幂的核心思路是二进制分解指数long long fastPow(long long a, long long n, long long mod) { long long res 1 % mod; a % mod; while (n 0) { if (n 1) res (res * a) % mod; a (a * a) % mod; n 1; } return res; }这里有两个细节必须注意。第一个是 res 的初值写成 1 % mod而不是直接写 1。为什么因为如果 mod 1任何数对 1 取模都等于 0初值直接写 1 就错了。第二个是乘法过程中可能溢出所以用 long long。在C里就算你用 long long如果两个 long long 相乘也可能溢出大数据量场景下得用更大的整数类型或快速乘但笔试中这类题一般不会把数给到那个量级。快速幂的变体非常多矩阵快速幂求斐波那契数列第N项、快速幂配合费马小定理求模逆元、快速幂用于RJFE——总之它是算法笔试中性价比极高的知识模块。建议你把“快速幂 模运算 模逆元”这条知识链一次性吃透配套刷几道LeetCode保证一劳永逸。2.3 最短路径、贪心、排序基础算法的高频考法这套题里还出现过一类“组合拳”式考察用一道题同时串起多个基础算法。比如最短路径问题题目会给你一张带权图让你求某个点到所有点的最短距离。最优解是堆优化的Dijkstra时间复杂度 O((VE)log V)。但如果你只在代码里写出了裸的Dijkstra而没有说明“为什么这题不能用SPFA”或者“为什么权值为负时Dijkstra失效”只能拿基础分。美团这类公司更看重你能不能严谨地分析算法的适用边界。再比如贪心常见考点是区间调度一堆活动有开始时间和结束时间最多能安排多少个不冲突的活动。标准解法是按结束时间排序依次选择最早结束且与已选活动不冲突的活动。很多人会背结论但说不清为什么必须按结束时间排序而不是按开始时间或持续时间排序。这里的核心逻辑是每次选择最早结束的活动能最大程度地为后续活动保留剩余时间。——这个证明过程比记住结论重要得多。排序算法在选择题里反复出现考察角度包括算法平均时间复杂度是否稳定原地排序典型考点快排O(n log n)不稳定是最坏情况O(n²)优化思路归并排序O(n log n)稳定否需要额外O(n)空间堆排序O(n log n)不稳定是建堆复杂度为什么是O(n)冒泡排序O(n²)稳定是有序数组下可提前停止这些细节看似基础但在选择题里很容易变成丢分点。比如“堆排序建堆的时间复杂度是多少”很多人的第一反应是 n 个元素依次插入堆每次 O(log n)所以是 O(n log n)。这个答案是错的。正确的建堆方式是从最后一个非叶节点开始向下调整整体复杂度是 O(n)。这个点我在当年笔试时就答错了印象极其深刻。3. 机器学习与深度学习基础题区分“背书选手”和“理解选手”的重灾区3.1 逻辑回归与线性回归的本质差异美团这套卷子的选择题里逻辑回归几乎是必考知识点而且考察方式不是简单的“逻辑回归用于分类还是回归”而是让你从原理层面判断一堆说法是否正确。逻辑回归虽然名字里有“回归”但它解决的是分类问题。它的本质是假设样本属于正类的对数几率是输入特征的线性函数log(p / (1 - p)) w·x b这个形式决定了逻辑回归输出的不是一个任意实数值而是经过sigmoid映射后的概率值。训练时最大化似然函数等价于最小化交叉熵损失而不是最小化均方误差。为什么不用MSE因为MSE对sigmoid的输出求梯度时会出现饱和区导致梯度消失、收敛极慢而交叉熵损失与sigmoid组合后梯度形式非常干净。这道题的高频变形还会问你逻辑回归中 L1 正则会得到什么效果答案是稀疏解因为L1正则化项在零点不可导更容易让部分权重变成0。L2正则化则会让权重整体变小但不强制为0。可以类比成两个不同风格的整理收纳师L1的风格是把用不上的东西直接扔掉L2是把每件物品都压缩到最小体积放好。3.2 K-Means、KNN等经典模型的考点细节这类基础模型在综合题里也出现过。给你一批商家特征让你用K-Means对商家分群然后问你K值怎么定初始中心怎么选对这个业务场景来说用什么距离度量K-Means的K值选择通常用肘部法则画出K值与聚类误差的曲线找拐点。但这个方案主观性较强实践中也会配合轮廓系数一起看。初始中心如果只靠随机选容易收敛到局部最优业界常用K-Means来初始化。距离度量则要看业务场景欧式距离适合特征量纲一致的数值型数据但商家特征里如果有“是否营业”“是否有优惠”这类二元特征马氏距离或余弦相似度也许更合适。KNN问得多的则是它的“三要素”K值选择、距离度量、分类决策规则。K值太小容易过拟合对噪声敏感K值太大则会把远处样本也纳入投票模型过于平滑。距离度量不使用曼哈顿距离还是欧氏距离取决于特征的实际分布。还有一个高频考点是用KNN之前一定要做特征标准化因为KNN依赖距离如果某个特征量纲特别大它会直接主导距离计算其他特征就白做了。3.3 模型评估与过拟合控制的核心考点美团很看重一个算法工程师会不会“评价自己的模型”。这部分的经典选择题是精确率和召回率的区别以及什么时候优先优化哪个指标。精确率Precision是“预测为正类的样本里有多少真的是正类”召回率Recall是“真实的正类里有多少被找出来了”。如果做外卖异常订单检测把正常订单误判为异常会伤害用户体验但漏掉异常订单又会导致平台资损。你要根据业务成本来决定优先优化哪个。这道题通常还会带上F1-score的计算以及ROC-AUC为什么比准确率更适合样本不平衡场景。过拟合控制也是热门考点。常考的有交叉验证、正则化、早停、Dropout深度学习、数据增强。需要理解它们的本质共同点——都是给模型增加约束或噪声降低模型对训练集的依赖。很多人只会列方法名称但如果问你“为什么L2正则化可以缓解过拟合”只说“因为它让权重变小”是不够的要能解释到“权重变小意味着模型输出对输入的变化不那么敏感函数更平滑”这个层次。关于深度学习那几年的笔试涉及的还比较浅主要是反向传播的基本概念、激活函数的作用、CNN的卷积核参数量计算等等。比如给你一个 32×32×3 的输入用 10 个 5×5×3 的卷积核做步长1的卷积输出尺寸是多少、参数量是多少。这类题只要会算就行没有太多弯弯绕绕。4. 业务场景综合题怎么让阅卷人看出你的算法落地能力4.1 外卖配送调度简化的思路示范美团业务场景题最典型的例子就是外卖配送调度。假设你手上有若干订单每个订单有取餐点、送餐点、期望送达时间你有一批骑手每个骑手一次可以携带多个订单怎么设计调度方案这种题的答题策略很重要。千万不要上来就开始写贪心或动态规划的细节而是先定义清楚问题。这道题的本质是一个带时间窗的车辆路径问题VRPTW是组合优化里出了名的NP难问题。你在笔试中不需要给出完美解法但你需要展现出“能把复杂问题拆解成可处理子问题”的能力。我当时给的思路是分层处理第一步做单量预测和区域划分把大问题拆成每个城区的小问题第二步在单城区内做路径规划用贪心或插入法先生成一个可行解再用局部搜索或模拟退火优化第三步做实时调度兜底处理超时压力大的订单。这里可以适当提一下粒子群算法。有同学在配送路径优化里用粒子群算法把每条可能的配送顺序编码成一个粒子通过不断逼近局部和全局最优位置来更新路径方案。这类启发式算法笔试里不需要写完整实现但能说出“为什么用启发式算法为什么不用精确算法”就能加分。答案是VRPTW精确算法能解的小规模问题在真实外卖场景下几乎不成立真实场景有几百个订单和几十个骑手必须牺牲最优性换时效。4.2 数据倾斜与样本不平衡的业务化解法另一个常见综合题是异常订单检测或用户行为预测这类问题一定会遇到样本不平衡。比如异常订单占比可能只有千分之一直接建模的话模型会学出一个“永远预测正常”的废物模型。在答这类题时往这几个角度展开通常比较稳妥数据层面对少数类做上采样SMOTE、对多数类做下采样、用异常检测算法做半监督打分算法层面选择对不平衡鲁棒的模型或者使用代价敏感学习让少数类分类错误的惩罚更大评估层面不用准确率改用PR曲线、AUC、召回率等指标因为准确率在极端不平衡下没有参考意义。这道题更看重的是你有没有意识到“纯算法解法是不够的”。我当时还答了业务侧的兜底策略比如人工审核队列、规则引擎拦截、实时风控模型融合面试官给的反馈是“有全局意识”。后来我回想这一类综合题其实是在模拟真实工作场景算法工程师不是孤立地调参而是要设计整套系统方案。5. 秋招算法笔试备考路线与避坑建议5.1 刷题路线和时间分配如果你现在才开始准备算法岗笔试不要慌但要有节奏。以三个月为周期的话我建议这样分配阶段时间核心任务预估题量基础巩固第1个月数组、链表、栈、队列、哈希表、递归每天3-5题专项突破第2个月树、图、动态规划、贪心、排序与搜索每天2-3题重点吃透套路真题模拟第3个月限时做整套笔试题复盘错题每周3套左右刷题不要追求数量要追求“见过的题能归类”。比如动态规划你把“背包”、“最长上升子序列”、“编辑距离”这几个经典模型吃透就能覆盖80%以上的DP题。遇到新题时先判断它属于哪个模型而不是硬碰硬地想状态转移方程。数据结构和算法之外要刻意留出时间准备机器学习的理论基础。很多同学在LeetCode上很强但遇到“L1和L2的区别”这种问题反而说不到点子上。你要能把逻辑回归、SVM、决策树、K-Means、KNN这些经典模型从原理到应用都讲明白最好能自己推导一遍损失函数和梯度更新过程。5.2 笔试现场容易踩的坑我当年和身边同学一起参加过多场笔试总结出几个高频翻车点这里直接列出来给你们避坑。第一题目定义不看清。就像前面说的KMP的next数组定义类似的还有堆排序的“大根堆还是小根堆”、Dijkstra的“节点编号从0还是从1开始”。这些细节往往藏在题目描述的最后一行但影响整个答案。我的习惯是读题时把关键定义圈出来不给自己“想当然”的机会。第二忽略数据范围。笔试编程题通常会给数据范围直接决定你选什么算法。看到 n 1000那 O(n²) 可以接受看到 n 10^5就要上 O(n log n) 甚至 O(n)。如果没注意到范围按大范围设计算法是浪费按小范围设计算法则直接超时。第三样例过了不等于能AC。编程环境里给的样例通常是构造出来的“良性输入”隐藏了很多边界列表为空、只有一个节点、数值为0、全相同元素。核心技巧是自己在草稿纸上补几个边界测试用例再提交。第四代码里不做防御性处理。比如快速幂里 res 1 % mod这个看似无关紧要的细节就是专门对付边界用例的。还有人指针忘了判空、数组下标越界、递归没有终止条件导致爆栈。这些在笔试环境里都是致命错误因为调试时间有限跑挂了很难快速定位。我在实际笔试中还有一个习惯就是编程题先用小规模数据在脑子里手动跑一遍代码逻辑确认没有明显错误再提交。这样看起来多花了一点时间实际上省掉了反复试错的笨功夫。如果你把这套2017年美团题单认真做完并复盘再加上以上这些考场经验面对大多数互联网公司的算法岗笔试都会有底气很多。剩下的就是把时间花在刀刃上基础算法多动手推演机器学习原理多问自己为什么业务场景题多练习用结构化方式表达方案。这几件事做到了笔试这一关就稳了。