蚂蚁算法岗笔试真题复盘:题型分布、考点与踩坑指南
3月中旬手机弹出一封邮件——蚂蚁集团2024春招算法岗第一批笔试通知。考试时间定在周六上午10点全程2小时在线监控。我当时正在刷LeetCode的贪心题看到邮件第一反应是“终于来了”第二反应是“第一批笔试题型到底什么样”。说实话算法岗这种岗位的笔试在没有面经参考的情况下准备起来非常没有方向感因为你不确定它考的是纯算法题、机器学习基础还是两者混着来。等真正做完整场笔试再和同批次的朋友们复盘了一圈我才算把这场笔试的套路摸清楚。这篇就当是给后面准备蚂蚁算法岗的同学一份参考把我这批次遇到的真题回忆版、考点拆解和踩坑细节都写出来能帮一个是一个。1. 这场笔试的组织形式与题型分布1.1 在线考试平台与考试环境进考场前先说说平台。蚂蚁这批用的是牛客网的企业笔试系统考前半小时开放登录登录之后系统会让你做环境检测包括摄像头、麦克风和浏览器版本。这里有个细节容易被忽略系统明确要求使用Chrome浏览器而且开着原生无痕模式反而可能触发警告。我当时用的Chrome提前调试了摄像头权限整个过程没出问题。但同批次有个朋友用的Edge进入考试后画面黑屏折腾了好几分钟才恢复正常白白浪费了宝贵的答题时间。考试全程是有监考的浏览器会记录切出页面的次数和时长如果频繁切到别的标签页后台会标记异常。我的建议是考前把所有辅助工具放在另一台设备或者打印出来别在考试电脑上开任何无关页面哪怕是查资料也不要冒险。远程笔试这东西宁可保守到极致也不要拿诚信记录开玩笑。1.2 题量与时间分配预判这批次我的真实题量是15道选择题单选多选混合 2道编程题总分100时长120分钟。选择题每题大概1分多一点编程题一道是中等难度大概LeetCode Medium偏下一道是偏难的涉及状态压缩DP后面详细讲。这个题量看起来不算多两个半小时足够写完但实际做下来会发现时间非常紧张。选择题涉及的面很广从数据结构、排序算法到机器学习基础都有很多题不是一眼能看出答案的。编程题又要读题、设计算法、调边界条件、处理输入输出如果前面选择题磨磨蹭蹭后面编程题很容易崩盘。关于时间分配我个人的建议是选择题尽量控制在35分钟内完成不会的题先标记跳过不要恋战两道编程题分别预留40分钟和30分钟最后15分钟统一检查选择题和补漏。我这个顺序后面吃了亏第5章详细复盘。1.3 计分规则和筛选逻辑牛客网的笔试系统对编程题有部分分机制通过一部分测试用例会拿到对应的比例分不是只有AC和非AC两个结果。这一点容易被低估——很多人看一道题没有完整思路就放弃实际上用暴力方法拿部分分比空着不写强得多。蚂蚁的算法岗笔试刷人比例确实不低从同批次考生的反馈看进面的基本条件应该是选择题正确率较高且至少完整AC一道编程题。但也不是说一定要两道全对选择题全对、编程题AC一道并有一定部分分的同学有不少也进了面。另外有个信息值得留意蚂蚁算法岗的笔试并不区分具体团队不管是投AI平台、OceanBase还是其他算法方向这一轮笔试用的是同一套题。真正分方向是笔试之后的事面试官会根据你的简历和意向团队重新评估。所以准备阶段不要被“我是做AI平台的是不是要多看深度学习”这种想法带偏这一轮笔试考的就是通用的算法基本功。2. 选择题考点复盘机器学习与数据结构各占多少2.1 机器学习/深度学习相关题选择题里机器学习相关的题大概占了5~6道这是算法岗笔试的特色也是区别于纯后端开发笔试的地方。记忆比较深的一道题是给了一个模型在训练集上AUC 0.99、测试集上AUC 0.72的对比问最合理的改善手段是什么选项有降低模型复杂度、增加L2正则化、增大训练数据、换一个更复杂的模型。这题考的是过拟合的基本判断——训练集极高、测试集低典型的过拟合答案肯定是正则化、降低复杂度、补充数据这一类选更复杂的模型反而是火上浇油。这类题目本身不难难在这是一道多选题少选一个就扣分需要有清晰的边界判断。还有一道和L1/L2正则化相关问L1正则化为什么能让模型参数变得稀疏。答案的核心在于L1的惩罚项在0点处不可导优化过程中参数很容易被压到精确的0而L2的梯度在参数接近0时会变小参数只是趋向0但不会成为严格的0。这道题不是死记结论就能答对的需要理解两种正则化对梯度下降过程的影响差异我在准备阶段刷过相关概念还算顺利。另一道印象深刻的题考的是聚类算法。题目大致是K-means算法在迭代过程中如果某个簇在分配步骤后没有样本点应该怎么处理。选项包括重新初始化该簇中心、直接保持原中心、将该簇删除并减少簇数、随机将其他样本点分到该簇。正确做法是重新初始化该簇中心这是K-means实现中很经典的空簇处理问题但在实际面试中反而很少有人会专门准备这个细节。2.2 数据结构与算法基础题数据结构算法这块的选择题有4~5道几乎覆盖了最常见的考点。有一道题考KMP算法的next数组模式串是abacaba问其next数组前缀函数形式是多少。计算过程是这样的前缀最长相等前后缀长度a0ab0aba1前缀a后缀aabac0abaca1前缀a后缀aabacab2前缀ab后缀ababacaba3前缀aba后缀aba所以答案就是 [0, 0, 1, 0, 1, 2, 3]。这里要注意不同教材对next数组的定义有差异有的是整体左移一位有的在末尾补-1但牛客网的题明确说了用前缀函数定义。看到这类题不要慌在草稿纸上老老实实画一遍就能算出来最怕的就是凭记忆背答案把几种定义搞混。排序算法稳定性的题几乎每场笔试都会出现。这道题问的是以下哪些排序算法是稳定的冒泡排序、快速排序、归并排序、堆排序。答案是冒泡和归并。快速排序因为分区操作是跨距离交换元素的会打乱相等元素的相对顺序堆排序同理建堆和堆调整的过程涉及一定范围外的交换稳定性无从保证。这些知识点属于八股但笔试还真会考复习的时候不能只看时间复杂度稳定性、空间复杂度、适用场景都得一起过。2.3 概率统计和工程场景题剩下几道题分布在概率统计和工程场景上。有一道概率题是这样的甲命中率为0.8乙命中率为0.7两人各自射击一次问至少有一人命中的概率。答案是1 - 0.2 * 0.3 0.94解法很直接考的是对立事件的转化。还有一道题结合了哈希表和数据库索引的工程场景问在什么情况下哈希索引会比B树索引更适合。选项包括范围查询、等值查询、模糊查询、排序查询。答案是等值查询。哈希索引的底层结构决定了它在等值查询上时间复杂度接近O(1)但无法支持范围扫描和排序。这道题对算法岗来说有点超纲但因为蚂蚁有OceanBase这类数据库背景笔试里出现数据库索引相关的题也不算意外。最后一道印象比较深的是关于AUC的题随机取一个正样本和一个负样本模型对正样本的预测分数大于对负样本预测分数的概率是多少答案就是AUC。这题考的是对评估指标的深层理解而不是单纯的公式记忆我在准备机器学习面试的时候看过AUC的几何意义和概率解释做起来比较从容。3. 编程题第一道看似贪心实则经典区间调度3.1 题目复述与思路推导编程题第一道题设不长大概是这样某平台有n场直播每场直播有固定的开始时间和结束时间同一个观众在同一时刻只能观看一场完整直播。给定n场直播的开始时间和结束时间问一个观众最多能完整观看多少场直播。n最大为10^5时间范围为0到10^9。看到这题的第一反应就是区间调度问题经典解法是按结束时间排序然后贪心选择最早结束且不与已选区间冲突的区间。但笔试现场要的不是背答案而是要把为什么按结束时间排序说清楚因为你的代码是按这个逻辑写的如果思路本身有漏洞边界情况处理错了AC率会很难看。为什么按开始时间排序不行我举个例子区间A[1, 10]区间B[2, 3]区间C[4, 5]。按开始时间排序A最早开始选了A之后B和C都冲突答案只能看1场但按结束时间排序B最早结束3点选B再选C4到5答案能到2场。贪心的核心逻辑是结束时间越早的区间给后面的区间留下的余量越大所以每一步“选择结束最早的可行区间”都不会让最优解变差——这就是区间调度问题里的替换法证明思路。写代码的时候我把这个逻辑在注释里写了一遍也算是对自己思路的一种确认。3.2 完整代码与边界条件def max_watched_shows(n, shows): # shows: list of (start, end) shows.sort(keylambda x: x[1]) count 0 current_end -1 for start, end in shows: if start current_end: count 1 current_end end return count这个代码逻辑很简单但边界条件有几个坑。第一个坑是时间范围很大时间戳可以是0所以current_end初始化为-1而不是0不然第一个区间从0开始的直播会被漏掉。第二个坑是区间左闭右开还是闭区间——题目说的是结束时间和开始时间相同的两场直播能不能连着看。那场笔试的表述是“相邻场次之间无缝隙即可”也就是start current_end就可以选这个细节如果不仔细读题很多人的解法会用成start current_end导致边界用例错一片。3.3 这道题的延伸思考第一题做完其实还有时间富余我顺手在草稿纸上想了想变体如果每场直播的收益不一样要求的是最大收益而不是最大场次那这个问题就不能用贪心解决了需要动态规划。按结束时间排序之后dp[i]表示前i场直播能获得的最大收益转移的时候需要二分查找最后一场不冲突的直播。这个变体在后续的面试里被面试官问到了笔试虽然没考但我建议准备笔试的同学把这类经典问题的所有变体都过一遍因为你永远不知道面试官会不会顺着笔试题往下追问。4. 编程题第二道状态压缩动态规划考验基本功4.1 题目的识别与状态设计第二道编程题明显难度上去一个档次。题目大意是有n个促销任务每个任务有前置任务约束、耗时都是1天和收益现在总共有limit天问能获得的最大收益。n的取值范围是1到20limit不超过15。看到n ≤ 20第一反应就是状态压缩DP。为什么因为20这个数字非常典型2^20约等于100万这个规模可以在合理时间内枚举所有状态。如果用一般的搜索或者递归复杂度会呈指数爆炸而n20的规模恰好是状压DP能handle的极限。状态定义我觉得很直观dp[mask] 表示已经完成了mask这个任务集合时能获得的最大收益。这里mask是一个二进制数第i位为1表示第i个任务已经完成。关键在于转移的时候如何判断前置任务是否都完成了——维护一个pre数组pre[j]是一个bitmask表示任务j的所有前置任务。那么任务j可以被加入当前状态mask的条件是(pre[j] mask) pre[j]含义是任务j的前置任务全部包含在mask中。4.2 完整代码实现def max_profit(n, limit, profits, pre): # profits: list of int, pre[j]: bitmask of prerequisites of task j size 1 n dp [-1] * size dp[0] 0 ans 0 for mask in range(size): if dp[mask] 0: continue # mask 中 1 的个数就是已经使用的天数每个任务耗时1天 if mask.bit_count() limit: continue ans max(ans, dp[mask]) for j in range(n): if (mask j) 1: continue if (pre[j] mask) pre[j]: next_mask mask | (1 j) dp[next_mask] max(dp[next_mask], dp[mask] profits[j]) return ans这里有个容易忽略的点dp不是简单地把所有可达状态都设为正数而是要逐步取最大值。因为同一个next_mask可能从多个不同的mask转移过来如果不用max可能丢掉最优路径。另外遍历mask从0到size-1的顺序是成立的因为每次转移都是往next_mask mask | (1 j)走这个值一定大于mask所以从小到大遍历天然满足无后效性。4.3 我在考场上的实际处理这道题我大概花了25分钟左右才完整调通。最开始我的代码没有检查mask.bit_count() limit这个条件导致算出来的结果可以通过小样例但在某些case里把超过limit天的方案也算进去了。后来在本地模拟数据的时候发现假设limit2n3如果三个任务互相没有前置关系理论上最多只能做两个任务但我的dp会把三个任务全做完的结果算出来。这个bug不是靠看代码能发现的必须构造一个规模很小的反例去验证逻辑。笔试做这种偏难的题我的心得是先写出能跑通小样例的朴素版再逐步优化而不是一上来就想一步到位。状态压缩DP本身就容易出错如果一开始就想着加各种剪枝和优化很容易把状态转移写乱。我在草稿纸上先画了一个n3的小例子把所有状态转移路径走了一遍确认没有遗漏之后才改写成代码提交。虽然花的时间长了一点但一次通过所有测试用例比反复交反复改要高效得多。5. 失败与教训我在时间分配上犯的错5.1 选择题上浪费了太多时间考完之后复盘我最大的失误就是选择题部分消耗过多时间。前面说过我的计划是选择题控制在35分钟内但实际上我在前10道选择题上就花了近30分钟原因是有几道题我不太确定反复在几个选项之间纠结。比如那道K-means空簇处理的题我当时在“重新初始化中心”和“保持原中心不变”之间犹豫了很久因为我记得某个库的实现是保持原中心但题目问的是理论上最合理的处理方式这个纠结本身就说明知识掌握得不够扎实。等到我开始做编程题时时间已经过去接近50分钟。第一题虽然顺利通过但第二题的状态压缩DP花了几分钟才想起来状态该怎么定义。如果选择题能再果断一点把不确定的题先用标记功能记录下来、最后再回头检查编程题的思考时间会充裕很多。5.2 读题不仔细导致的低级返工另一个浪费时间的点是第一道编程题。题目里“结束时间和开始时间相同即可看连续场次”这个细节我一开始没注意到代码用成了严格大于结果提交后没过第一个样例。回头重读题目才发现问题改一行就通过了。笔试时平台只提供最简单的编辑器没有本地IDE那种边写边跑的能力改完代码还要自己模拟输入去验证这个过程非常消耗时间。所以我能给的建议就是读题至少读两遍把时间区间边界的处理方式圈出来再动笔。很多时候不是题目难而是细节没看清。5.3 复盘后的改进策略笔试结束当晚我把所有不确定的题重新整理了一遍。对于选择题我把机器学习、数据结构和概率统计三块分别列了一个错题集发现薄弱点主要集中在机器学习的模型评估和细节处理上而不是纯数据结构。于是我后面两周的复习重心就放在了偏差方差、正则化、各类评估指标、聚类算法细节上这个方向和纯刷LeetCode是完全不同的准备路径。时间分配我也重新做了规划。如果再有类似的笔试我会给自己定一个硬性规则每道选择题最多2分钟超时先标记跳过编程题每个题先读两遍题再花5分钟在草稿纸上理思路不能一上来就写代码。这两条规则看似简单但实际操作中非常考验执行力。很多人笔试发挥不好不是因为实力不够而是因为时间节奏失控。第二天系统出了各题得分第一道编程题是满分第二道拿到了大部分测试用例的分数选择题具体错了几道没显示但最终结果是收到了后续面试通知。回头看这一轮笔试难度分布其实挺合理的没有偏题怪题考的都算是算法岗该有的基本功。唯一需要注意的是题量虽然不大但覆盖面广复习的时候不要只顾着刷题机器学习和概率统计的基础概念一定要过一遍。接下来我应该还会再写写面试那一轮的复盘先把笔试这部分记录在这里给后面准备的同学做个参考。