美团2017算法工程师笔试复盘:从KMP到逻辑回归的高频考点解析
2026/8/30 21:54:01 网站建设 项目流程

前两天整理旧电脑里的面试备份,翻出一份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"无(长度不能等于自身)0
"ab"0
"aba""a"1
"abac"0
"abaca""a"1
"abacab""ab"2
"abacaba""aba"3

所以前缀函数式 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 res

2.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((V+E)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年美团题单认真做完并复盘,再加上以上这些考场经验,面对大多数互联网公司的算法岗笔试都会有底气很多。剩下的就是把时间花在刀刃上:基础算法多动手推演,机器学习原理多问自己为什么,业务场景题多练习用结构化方式表达方案。这几件事做到了,笔试这一关就稳了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询