
我在互联网算法岗笔试这个圈子里泡了很多年也帮不少学弟学妹复盘过大厂的真题。今天想拿出来仔细聊聊的是美团2017年秋招算法工程师B的笔试。先说个结论这套卷子放在今天看依然不过时因为它几乎把互联网算法工程师笔试的经典考察维度都覆盖了——数据结构、排序、字符串匹配、贪心、动态规划再加上机器学习基础。如果你是准备大厂算法岗笔试的人不管目标是不是美团这份复盘都值得你花十分钟读完。笔试和面试不一样。面试还能看你的表达、项目经历和临场反应笔试就纯粹看硬功夫代码能不能跑通、边界条件想没想到、时间复杂度能不能压下来。而美团这类大厂笔试题的难度并不在于题目本身多偏多怪恰恰在于它们都长着一张“熟面孔”——你一看就会但一写就容易翻车。这恰恰是笔试最恶心的地方它考的从来不是“会不会”而是“熟不熟”。所以这篇文章我不打算只给答案而是把我拆解真题时的思考过程、踩坑记录和复盘心得全部分享出来重点讲清楚每一类题背后的考察意图。你照着这个思路去准备比盲目刷题要高效得多。1. 笔试整体设计与考察逻辑拆解1.1 笔试结构不是只有编程题很多人以为算法笔试就是写代码其实不是。美团2017年秋招算法工程师B的这套卷子题型大致分三块第一部分是基础选择题涉及数据结构、概率统计、机器学习基础第二部分是编程题大概三到四道从易到难排列第三部分是简答或者综合设计题会给一个业务场景让你给出解决方案。我当时看到这个结构的第一反应是这其实是三道关卡分别考察你的知识广度、代码硬实力和业务抽象能力。选择题不是白给的它筛选的是那些“背过八股文但没理解”的人比如给你一段代码问时间复杂度或者给你一个机器学习概念让你选错误的说法这种题非常考验基本功是否扎实。第二部分的编程题则是拉开差距的地方。根据我的复盘这套题目的难度曲线很典型第一题通常是简单的数组操作或字符串处理大概LeetCode Easy到Medium的过渡段中间一题会用到贪心或二分稍加变化就能卡住一批人最后一题则是标准的动态规划状态转移方程不复杂但边界条件极其容易写错。第三部分综合设计题容易被忽略但其实是区分“刷题机器”和“算法工程师”的关键。它不会让你手写一个没见过的算法而是给你一个偏业务的场景比如“外卖配送路径优化”“用户复购预测”之类的让你设计方案。这考的是你有没有做过真实项目能不能把业务问题抽象成数学模型。很多人在这里挂掉不是因为不会算法而是因为不会把问题说清楚。1.2 考点选型背后的“潜规则”这里我要说一个很多人没意识到的事大厂笔试考点选型是有规律的它不是在跟你炫技而是在筛“能干活的人”。美团当时的业务重心是本地生活服务比如外卖、到店、配送。这些业务的核心技术挑战是什么是海量数据下的路径规划、供需预测、排序推荐、风险控制。所以笔试考的算法都是这些业务场景的基础组件排序是推荐系统里做召回排序的基础贪心是路径规划里最常见的启发式策略动态规划则直接对应配送路径优化和资源分配问题字符串匹配在搜索、检索、关键词匹配场景里无处不在。反过来看有些算法非常经典但不太可能出现在这类笔试里比如偏控制领域的PID算法、粒子群优化等智能优化算法。我在复盘的时候跟几个同行聊过这个问题大家的共识是这些算法在互联网线上业务里用到的概率太低考察成本高区分度也不好所以大厂笔试基本不会碰。如果你在准备笔试千万不要花太多时间在这些花哨但非主流的算法上排序、二分、贪心、DP、字符串匹配、图的最短路这些才是高频考点。这个观察往深了说其实揭露了一个核心问题笔试是低成本、高并行的筛选手段它的目标不是选出“算法理论最强的人”而是选出“基础最扎实、在压力下最快写出可运行代码的人”。理解了这点你备考的重点就清晰了。2. 编程题核心算法与真题解题思路2.1 动态规划题状态定义比转移方程更关键我当时复盘这套笔试时印象最深的一道编程题大概是这样给定一个数组每个元素代表你当天能获得的收益但如果你选择在某天“工作”那么接下来的一天必须“休息”问如何安排才能获得最大收益。这题听起来像打家劫舍的变体但其实需要考虑的状态更多。先说第一反应。很多人一看这题就说“这就是打家劫舍”于是立刻写下dp[i] max(dp[i-1], dp[i-2] nums[i])。但这里面藏了个坑题目的限制是“工作后必须休息一天”也就是如果你第 i 天工作第 i1 天不能工作但第 i2 天可以。这和打家劫舍的“不能偷相邻两家”看起来一样实际上要考虑的是连续工作之间的间隔策略。我复盘时给出的解决思路是定义dp[i][0]为第 i 天休息时前 i 天能获得的最大收益dp[i][1]为第 i 天工作时的最大收益。转移方程如下dp[i][0] max(dp[i-1][0], dp[i-1][1]) dp[i][1] dp[i-1][0] nums[i]这个方程的逻辑是如果今天休息昨天工作或休息都可以如果今天工作那昨天必须休息。看着很简单对吧但很多人会漏掉初始化条件dp[0][0] 0、dp[0][1] nums[0]。一旦漏掉整个结果全错。我在实际写代码的时候还会纠结一个问题如果数组长度是0怎么办空数组直接返回0这个边界条件容易忘。还有如果允许连续休息多天那dp[i][0]的转移是对的不需要额外状态。这题想清楚了其实是在考察你对“状态机DP”的理解。美团这类公司很喜欢考状态机DP因为它能很自然地跟业务场景挂钩——比如股票买卖、任务调度、资源分配。处理这类题的核心不是背模板而是想清楚“有哪些状态”和“状态之间如何转移”边界条件最后再补。2.2 贪心与二分的组合应用这套卷子里还有一道让我印象深刻的题大致是有 n 个包裹需要配送每个包裹有重量和配送地址配送车有最大载重问最少需要几辆车才能把所有包裹送完。这个问题表面上是个装箱问题看起来像NP难但题目加了一个条件所有包裹的重量都小于等于车的最大载重并且包裹可以任意组合。有了这个条件解法就清晰了排序双指针贪心。思路是先把包裹按重量从小到大排序然后用两个指针一个指向最轻的包裹一个指向最重的包裹。如果最重和最轻的能装进同一辆车就一起装否则最重的单独装一辆车。这个贪心策略可以证明是最优的。我当时在复盘的时候回想起一个容易出错的地方指针移动的终止条件。如果只剩一个包裹直接加一辆车就好。如果最轻和最重不能装一起只移动右指针左边的不动。这个逻辑看着简单但在笔试环境下紧张起来很容易写成left 1; right - 1同时移动直接把结果搞错。def min_trucks(weights, capacity): weights.sort() left, right 0, len(weights) - 1 trucks 0 while left right: if left right: trucks 1 break if weights[left] weights[right] capacity: left 1 right - 1 else: right - 1 trucks 1 return trucks这题的延伸很有意思如果把“任意组合”换成“每组必须恰好两件”贪心就不成立了得用二分答案贪心判断。但美团这个题没有绕到二分那一步它考察的就是最基础的贪心直觉和双指针实现。所以复盘后我最大的感悟是不要把简单问题想复杂但也要能识别简单问题里藏着的小陷阱。2.3 字符串匹配与KMP的考察思路再来说字符串。笔试里有一道很经典的字符串匹配题不是让你调库实现find而是让你实现一个简化版的模式匹配。这里就牵出了热搜词里的 KMP 算法。虽然美团这套题没有直接要求写 KMP但如果你只会暴力匹配遇到长字符串用例就会超时这才是真正想考察的“隐性知识点”。KMP 的核心是前缀函数也叫 next 数组。热搜词里有个例子“对于模式串 pabacaba求 next 数组”这正好是最经典的考察方式。我建议你彻底搞懂 next 数组的定义和求法而不是只背代码。next[i] 通常定义为模式串 p 的子串 p[0..i] 中最长的相等真前缀和后缀的长度。注意是“真前缀”不能是整个子串。以p abacaba为例如果按“从0开始、且 next[i] 表示 p[0..i] 的最长相等前后缀长度不包含自身”来算过程是这样的i0字符a真前后缀为空next[0]0i1子串ab前缀a、后缀b不相等next[1]0i2子串aba前缀a和后缀a相等next[2]1i3子串abac没有相等前后缀next[3]0i4子串abaca前缀a和后缀anext[4]1i5子串abacab前缀ab和后缀abnext[5]2i6子串abacaba前缀aba和后缀abanext[6]3写出 next 数组就是[0, 0, 1, 0, 1, 2, 3]。如果你在不同的资料里看到不同的结果往往是定义差异有些版本把 next 数组整体右移一位或者从1开始计数。这里我强烈建议考试时先注释里写清楚自己的定义这样即使结果跟标准答案对不上阅卷人也能看懂你的思路。KMP 匹配时的核心思想是主串指针不回溯只回溯模式串指针。这个“不回溯”的特性让 KMP 在最坏情况下也能保持 O(nm) 的时间复杂度而暴力匹配最坏是 O(n*m)。在大厂的笔试里字符串长度往往给到 10^5 甚至 10^6这时候暴力必挂KMP 才能过。我多年复盘真题的经验是大厂笔试考 KMP 很少直接说“请你实现KMP”而是给你一个看似能用暴力解决的字符串匹配题然后数据范围卡死暴力解法。所以备考阶段务必要把 next 数组的推导练到手熟不能只背模板。3. 数据结构、排序与复杂度分析要点3.1 排序算法横向对比不止是背时间复杂度笔试的选择题里排序算法几乎年年出现。美团这套题也不例外。选择题会给你几个排序算法的描述让你选出错误的选项或者给一组数据让你判断用了什么排序算法。这时候如果只背“快排平均 O(n log n)”是很容易翻车的。我复盘时整理了一张高频对比表建议你收藏排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性特点冒泡排序O(n^2)O(n^2)O(1)稳定简单但慢几乎只在教学中出现快速排序O(n log n)O(n^2)O(log n)不稳定实际最快但最坏情况要防归并排序O(n log n)O(n log n)O(n)稳定稳定排序首选适合链表堆排序O(n log n)O(n log n)O(1)不稳定原地排序但常数大插入排序O(n^2)O(n^2)O(1)稳定接近有序时接近 O(n)希尔排序约 O(n^1.3)O(n^2)O(1)不稳定插入排序的改进版这里最容易被忽略的是“稳定性”。我见过太多人在笔试里栽在稳定性判断上。稳定性的意思是如果两个元素值相等排序后它们的相对顺序不变。冒泡和归并是稳定的快排、堆排、选择排序是不稳定的这个结论要刻在脑子里。另一个高频考察点是“快排最坏情况”。很多人以为快排永远是 O(n log n)其实不是。当数组已经有序且你每次选第一个元素作为 pivot 时快排会退化成 O(n^2)。这对应刷题热词里的“排序算法”和“算法流程图”的考察方向。大厂笔试喜欢在这里做文章给你一个几乎有序的数组问哪种排序最快答案是插入排序而不是快排。3.2 关于时间复杂度的计算与面试官陷阱算法工程师笔试的选择题里时间复杂度计算几乎是必考题。但这部分却常被刷题同学忽视。因为 LeetCode 只要求代码能过不会专门考你“这段代码复杂度是多少”但笔试选择题就会。我印象里美团这套卷子有一道题大概是这样的给定如下代码片段问时间复杂度是多少。i 0 while i n: j 0 while j n: j 1 i * 2这段代码里内层循环是 O(n)外层循环每次 i 乘以 2所以外层执行 log n 次。总复杂度是 O(n log n)。看起来很直接对吧但如果你没注意到i * 2而以为是i 1就很容易答成 O(n^2)。这种题就是考你有没有仔细审题。还有一个经典陷阱是“斐波那契数列递归实现的时间复杂度”。如果你写return fib(n-1) fib(n-2)这个递归树是一棵满二叉树时间复杂度是 O(2^n)不是 O(n^2)。但如果用备忘录或者动态规划就能降到 O(n)。大厂很喜欢拿这个做文章因为它既考递归思想又考复杂度分析。我在复盘时特别注意了这点笔试选择题里的复杂度题其实是在考察你“写代码时有没有复杂度意识”。因为真实业务里一段代码上线前必须评估它在海量数据下的运行时间。如果没有这层意识笔试容易挂真实工作中更是要出问题的。3.3 哈希表与链表隐藏的考点除了排序和复杂度数据结构基础题也占据了不少分值。我当时复盘时总结了一下美团这套卷子的数据结构考点主要集中在哈希表、链表和二叉树。哈希表几乎是所有笔试必考内容它最常考的“哈希冲突解决办法”开放定址法、链地址法、再哈希法。选择题可能会给你一个场景比如“哈希表已有 70% 满了哪种冲突解决办法性能下降最严重”这就需要你理解不同方法的特性。链表和二叉树相关的题目通常不会太深但会考察你对指针操作的理解。比如反转链表或者判断二叉树是否平衡。别觉得这些太基础真实笔试里在限时压力下单链表反转写错的概率远超你想象。我见到过不少候选人写反转链表时直接把next指针弄丢导致死循环。二叉树部分最常考的是三种遍历方式的前中后序以及层序遍历。尤其要掌握递归和迭代两种写法。我有个习惯每次复习数据结构都会把二叉树前序遍历的递归版和迭代版都写一遍。这个练习成本很低但笔试时能帮你节省大量思考时间。4. 机器学习与深度学习考点解析4.1 经典模型与损失函数选择题的重灾区美团算法工程师B的笔试不会只考编程题机器学习基础占的比重非常大。这套卷子里有关机器学习的选择题大概占了三成左右而且相当“抠细节”。比如问你对数损失函数LogLoss的性质或者问 SVM 中核函数的作用再或者问 L1 和 L2 正则化的区别。先说损失函数。交叉熵损失是分类问题里最常用的损失函数它的核心特性是当预测概率接近真实标签时损失趋近于0当预测错误且置信度高时损失非常大。这个“置信度高但错误”的情况恰恰是交叉熵最凶狠的地方——它能有效惩罚那些过度自信的错误预测。关于 L1 和 L2 正则化我从笔试角度给你一个速记L1 正则化会把参数推向稀疏很多参数变成0适合做特征选择L2 正则化会把参数推向接近0但不等于0适合防止过拟合。两者的本质区别在于惩罚项的绝对值与平方。如果你业务里特征数量特别大想压缩模型体积优先考虑 L1。还有一个经常被拿来出选择题的概念偏差与方差。高偏差意味着模型过于简单连训练集都拟合不好也就是欠拟合高方差意味着模型过多地记住了训练集的噪声也就是过拟合。这个问题我在后面会展开讲。4.2 过拟合、正则化与模型评估方法过拟合是算法工程师面试笔试的高频话题。这套笔试的简答题里就有一道类似于“如何判断模型过拟合如何解决”的题。很多人回答“增加数据、加正则化”这当然没错但太浅了。我倾向于这样回答首先通过训练误差和验证误差的对比来判断过拟合。如果训练误差很低、验证误差很高两者之间的差距在增大就是过拟合的典型信号。然后针对性地给出解决方案增加训练数据尤其是有代表性的数据、降低模型复杂度、加入正则化、使用早停法、做数据增强。如果你说的场景是深度神经网络还可以提到 Dropout 和 Batch Normalization。另外还有一个必考概念交叉验证。最常见的 K 折交叉验证是把训练集分成 K 份每次用 K-1 份训练、1 份验证轮流做 K 次最后取平均。它的主要用途是更稳定地评估模型效果尤其是在数据量比较小的时候。这里我多说一句笔试选择题里经常会出现“为什么用交叉验证而不用单一训练集/测试集划分”。答案是单一划分在数据量少时容易受偶然因素影响某一次划分运气好或者运气差都会误导你对模型真实能力的判断。交叉验证通过多次训练和验证把这种偶然性平均掉了。4.3 深度学习的早期概念卷机神经网络与激活函数2017年的时候深度学习已经火得不行了所以美团这套笔试里也出现了深度学习的题目。不过考察的都不深当年考得最多的还是卷积神经网络和激活函数。激活函数的选择是一个经典考点。Sigmoid 函数在深层网络里容易导致梯度消失因为它的导数在两端趋近于0反向传播时梯度一再相乘很快就没了。ReLU 因为正区间导数恒为1能很大程度上缓解梯度消失但缺点是负区间输出恒为0可能导致神经元死亡。Leaky ReLU 和 ELU 就是针对 ReLU 这个缺点做的改进。还有一道容易被考到的概念题是卷积操作的“感受野”。CNN 里越深的层单个神经元能看到的原始输入区域越大。这个感受野的大小跟卷积核尺寸、步长和层数有关。理解这个对做图像类业务很有帮助。深度学习部分虽然不像编程题那样直接写代码但概念题错一个就可能影响整体排名。复盘完这套题我的感受是如果你把机器学习基础概念掌握扎实了这部分拿分效率很高因为它不像编程题那样受临场状态影响。5. 常见问题与考场实战避坑5.1 时间分配与做题顺序先保分再攻坚笔试的时间分配是我最想跟你强调的一点。美团这套卷子的题量不小包含选择题、编程题和简答题如果你心态不好很容易在某一道编程题上卡太久导致后面的题没时间做。我的建议是先快速浏览一遍全卷用一两分钟判断每部分的难易程度。选择题如果遇到拿不准的先标记一下不要恋战直接跳过去。编程题从最简单的开始做先把该拿的分拿到手。最后再回来攻难题。具体时间分配上我倾向于选择题控制在30%的时间编程题控制在50%的时间简答题留20%。因为编程题需要调试未知因素最多。如果你在某一题上写了20分钟还没调通果断先放下去做后面的简答题。千万别在一棵树上吊死。5.2 细节错误与边界条件笔试翻车重灾区我觉得笔试翻车最典型的原因不是不会做而是细节处理不到位。这里我把常踩的坑整理成一个速查表也是我每次给同学做笔试辅导时必发的内容坑点现象解决方案数组越界访问arr[n]而不是arr[n-1]所有循环变量写完后自查一遍空数组/空串直接访问s[0]报错函数开头判断长度是否为0整数溢出两数相加超过 int 范围用 long 或 Python 无需担心指针丢失链表反转时next被覆盖先保存后继节点再改指针全局变量污染多组测试数据之间状态未重置循环内重新初始化所有变量栈溢出递归深度过大导致爆栈改迭代或增加递归终止条件这里特别说一下“多组测试数据”这个坑。大厂笔试的编程题通常允许输入多组用例。很多人习惯在本地只测一组数据结果一跑线上就出错往往就是全局变量没有重置。我每次做题前都会强迫自己所有变量声明尽量放在循环内部不为别的就为了隔离污染。另一个容易忽略的是输入输出的处理。笔试环境里输入格式通常有明确说明比如“第一行一个整数n第二行n个整数”。不要用input()一把梭而是用拆分后再转类型。如果读取超时考虑用sys.stdin批量读取。5.3 不会的题怎么办写暴力先拿部分分这里我想分享一个被很多人忽视的策略部分分。大厂笔试的编程题经常有多个测试点有些测试点数据规模很小即使你的算法复杂度高到天上去只要答案对就能拿到一部分分数。所以我的建议是如果一道题想不出最优解先写一个暴力解法保证小数据能过。然后再在这个暴力解的基础上去优化。这比你对着空编辑器纠结半小时要明智得多。回到美团这套题我当时复盘时发现有一道动态规划题最笨的写法是直接递归搜索虽然会超时但数据小的时候能过大概能拿一半分。如果加上备忘录就能拿满分。这个思路放到今天的笔试里一样适用。很多同学觉得“不会做就不写”其实白白丢了一堆分。5.4 总结一段我在实战中的个人体会笔试这个东西说穿了拼的是“熟练度”三个字。美团2017年这套算法工程师B的真题放到今天来看难度并不算顶尖但它覆盖面广、细节陷阱多是一套非常适合拿来练手和检验基本功的卷子。我后来每一次给准备跳槽算法岗的朋友做模拟测试都会拿当年的题型当模板出题因为它是真的能看出一个人是不是“代码写得熟、概念记得牢、边界想得到”。如果你正在准备这类笔试我真心建议你找一套往年的真题严格按照考试时间做一遍。然后不要只是看答案对不对而是把每道题背后的考点都挖一遍问自己三个问题这题在考什么为什么这样考如果我是出题人我会在哪里设陷阱把这套复盘方法坚持下去你会的题目会越来越多而且更重要的是你的“算法直觉”会越来越准。