☰
2024小米算法岗笔试复盘:高频考点与编程题实战解析
2026/10/19 21:28:20 网站建设 项目流程

每年秋招,算法岗笔试都是筛人最狠的一关。今年小米集团的算法岗,我报的是第二批笔试,整个过程从报名到考试间隔不到一周,准备时间其实很紧。考完我把题目和整个答题过程复盘了一遍,今天这篇就把第二批算法岗笔试的题型结构、高频考点、编程题解法以及踩坑细节全部分享出来。如果你也打算冲小米或者其他互联网大厂的算法岗,这篇复盘应该能帮你建立一个相对完整的备考坐标系。

先说结论:小米算法岗笔试不是那种“偏题怪题满天飞”的风格,整体上非常重视基础,数据结构、经典算法、机器学习理论都会覆盖,编程题考的是能不能在有限时间内写出可运行的干净代码。和第一批相比,第二批的题型和难度没有本质差异,但部分选择题的细节抠得更深,编程题的数据范围更大,明显是在逼你考虑时间复杂度。整个过程下来,我的感受是:把基础打扎实,比刷一堆偏题有效得多。

1. 2024秋招小米算法岗笔试整体印象与考情分析

1.1 笔试形式、平台与时间安排

今年小米的笔试统一走在线笔试平台,算法岗第二批仍然是双机位监控:正面电脑摄像头要拍到脸,手机开二机位放在侧后方,考试过程中不能切屏,切屏次数多了会被系统记录,严重的直接判作弊。这一点要特别提醒一下:考试前至少留半小时调试设备,浏览器建议用Chrome或Edge的较新版本,很多在线笔试平台对旧版浏览器兼容性很差,万一中间崩了非常影响心态。

时间安排是固定时长,从开考开始倒计时,到点自动交卷。我参加的那场总时长大概是两个小时左右,题量并不算大,但因为包含编程题,时间其实一点都不宽裕。题目在开考时统一放出,可以自己选择先做哪部分,不过大部分人的习惯是先把选择题快速扫一遍,再集中精力搞编程题,最后留时间写简答和场景题。

笔试平台本身支持本地IDE编译,也支持在网页里直接写。建议提前准备好输入输出模板,尤其是用C++或者Java的同学,把ios::sync_with_stdio(false)、Scanner这些常用模板背熟,能省下不少时间。Python的同学则要注意sys.stdin.read()这类批量读入方式,避免在大数据量输入下卡在读入环节。

1.2 第二批与第一批的差异

从第一批放出来的面经和讨论看,小米算法岗几批笔试的题型框架是一致的,都是“选择题 + 编程题 + 简答/场景题”的组合,但第二批有几个明显的调整:

  1. 选择题中机器学习理论的比例更高,尤其是正则化、损失函数、模型评估这几个方向。
  2. 编程题的测试数据范围更大,部分题目需要用到long long,直接用int很容易溢出。
  3. 场景题不再是简单的“介绍一下你熟悉的模型”,而是给了具体业务限制,比如数据量、延迟要求、冷启动问题,让你给完整方案。

这意味着第二批更看重候选人的工程落地意识,而不是单纯刷题家。用一句通俗的话说:第一批问的是“你会不会”,第二批问的是“你敢不敢真上项目”。

1.3 题型分布与分值权重参考

下面这张表是我根据自己的答题经历和网上讨论整理出来的,具体批次可能有出入,但总体框架是可以参考的:

题型题量大致分值建议用时
单选/多选题15题左右30分25~30分钟
编程题2题50分70~80分钟
简答/场景设计题1题20分15~20分钟

选择题是单选多选混着出,多选少选错选都不得分,这个在做题时一定要看清题干要求。编程题是ACM模式,也就是需要自己处理输入输出,不是力扣那种只写核心函数的形式。简答/场景题是文字作答,没有标准答案,但很看答题结构和细节。

从分值占比就能看出来,编程题是笔试的绝对核心,两题就占了50分。选择题虽然分值不高,但它决定了你能不能拿到一个基础分,错太多的话即使编程题AC了也很难进面试。

2. 高频考点盘点:数据结构、经典算法与模型原理

2.1 数据结构基本功:KMP、堆、并查集这些“老熟人”

小米笔试的选择题里,数据结构考察得非常细致,而且特别喜欢在经典算法上做变形。举个例子,模式串匹配的KMP算法,很多人刷题时会直接跳过,觉得自己“会用库函数就行”,但笔试它就敢直接考next数组的手算推导。我印象里有一道题给了一个模式串,要求写出某个位置失败后模式串应该移动到哪里,本质上就是考KMP的next数组计算。

这里说一个KMP的快速手算方法。next数组有很多种定义版本,小米题目里如果没特别说明,一般用的是“对于模式串的第i个位置,next[i]表示该位置之前的子串中最长相等前后缀的长度”。例如模式串p = "abacaba",我们手算时逐个前缀分析:

前缀最长相等前后缀next值
a无0
ab无0
abaa1
abac无0
abacaa1
abacabab2
abacabaaba3

平时可以多练几个字符串手算next数组,考场上别指望编译器帮你算,选择题必须一眼能看出来。

除了KMP,高频数据结构还包括:栈和队列的单调性应用(单调栈、单调队列)、堆的建堆与堆排序复杂度、并查集的路径压缩与按秩合并、哈希表的冲突处理方式、LRU缓存的实现思路。其中LRU在简答题里出现过好几次,建议把“哈希表 + 双向链表”的实现方案背熟,能讲清楚get和put的时间复杂度为什么是O(1)。

2.2 经典算法:快速幂、DP、贪心、Dijkstra与拓扑排序

经典算法方面,本次笔试的重头戏是动态规划、贪心和图论算法,选择题和编程题都会涉及。

快速幂是小米笔试常考的基础算法,看起来简单,但很多人写起来会忽略指数为0、底数取模这些边界。模板我就不贴了,提两个要点:一是二进制拆分的思路,把指数按二进制位分解,底数不断自乘;二是乘法结果要随时取模,防止溢出。这类题一旦考到,基本都是给分题,丢分很可惜。

动态规划考的范围很广,从经典的背包问题、最长递增子序列、编辑距离,到网格路径类问题都有可能出现。笔试里的DP题通常不会直接告诉你“这是DP”,而是给一个看似是搜索或贪心的题目,让你自己发现最优子结构。所以做题时第一反应不要急着写暴力搜索,先看数据范围,如果n在1000以上,大概率是O(n^2)的DP;如果n在10^5级别,那就要考虑O(n log n)的优化,比如LIS的二分优化、状态压缩、单调队列优化等。

贪心算法在选择题里经常以“下列说法正确的是”的形式出现,考察你是否知道某个经典贪心问题的反例。比如“每次选收益最大的任务”不一定能获得最大总收益,这类题目需要你对贪心策略的正确性有判断能力,不能凭感觉选。

图论方面,Dijkstra算法的变体、拓扑排序判断有向图是否有环、并查集判连通分量,都是高频考点。Dijkstra注意堆优化版本的复杂度是O((V+E)logV),选择题会反复考;二分图匹配如果出现,大概率是考最大匹配等于最小点覆盖的结论,不一定会让你手写匈牙利算法。

2.3 机器学习与深度学习基础:从XGBoost到损失函数

算法岗笔试和纯开发岗笔试的最大区别,就是必然有一批选择题考机器学习理论基础。今年第二批在这方面出得很细,印象比较深的有这么几类:

第一类是模型对比题,比如“XGBoost相比GBDT做了哪些改进”。这种题不能只背结论,得能从原理上说清楚:XGBoost在目标函数里加了二阶泰勒展开,支持自定义损失函数;加入了正则项控制模型复杂度;特征粒度上支持并行;分裂点的选择可以近似直方图算法加速;还能处理缺失值。如果只是背“XGBoost比GBDT快、效果好”这种话,很容易在多选里漏选。

第二类是损失函数和正则化的细节。比如交叉熵损失对Softmax输出的梯度形式,L1和L2正则化的区别,为什么L1能产生稀疏解,Dropout在训练和推理时的行为差异。这些概念看起来基础,但出题人非常擅长在选项里混入“看起来对”的错误表述,比如“Dropout推理时也要随机失活”,这种明显错误的选项其实就是送分点。

第三类是模型评估指标。准确率、精确率、召回率、F1、AUC、ROC曲线是必考项,但这次还考了类别不平衡场景下应该优先看哪个指标,以及如何在数据层面做采样。这提醒我们复习时要关注“指标在什么场景下失效”,而不仅仅是背公式。

深度学习部分,卷积神经网络和Transformer是重点。选择题会考CNN感受野的计算、BN层在训练和推理时的区别、注意力机制的公式(Q、K、V怎么算),以及Transformer里的位置编码为什么重要。建议把这些模型的经典结构图自己画一遍,输入输出维度手推一遍,对做选择题非常有帮助。

2.4 优化与控制类算法:粒子群、模拟退火、卡尔曼滤波这些“边角料”

小米笔试还有一个特点,就是偶尔会冒出一两个看起来超出常规算法体系的概念题,比如粒子群算法、模拟退火、卡尔曼滤波、PID控制。这些在AI算法岗的日常工作中不一定用得上,但出题人可能觉得“搞算法的应该有所耳闻”。

我的建议是:不需要深入源码级掌握,但要能说出核心思想。粒子群算法的核心是“群体协作 + 个体历史最优 + 群体全局最优”,模拟退火的核心是“以一定概率接受更差解,从而跳出局部最优”,卡尔曼滤波的核心是“预测 + 更新,融合先验和观测”,PID控制的核心是“比例、积分、微分三项组合调节”。这类题在笔试里属于“知识面题”,会就是会,不会也能靠常识排除部分选项,花太多时间深挖不值得。

音频重采样、图像锐化的拉普拉斯算子、Sobel边缘检测这类题目,可以归为“数字信号处理/图像处理常识”。如果投的不是CV或音频算法岗,这类题占比很低,但至少要知道拉普拉斯算子是二阶微分算子,Sobel是一阶微分算子,都属于卷积核操作。

3. 编程题实战:从读题到AC的完整推演

3.1 编程题整体风格与输入输出套路

小米笔试的编程题是ACM模式,需要自己处理标准输入输出,而且提交后按测试用例得分,不是“AC了就满分,错了就0分”。所以即使是暴力解法,只要部分用例能过,也能拿到一些分数。

两道编程题一般是一道DP、一道贪心或图论,难度介于力扣中等和困难之间。数据范围比较大,第一道题通常n在1000到10^5之间,第二道题可能到10^5甚至更大。这种数据范围其实已经暗示了答案的复杂度要求:不允许O(n^2)暴力过第二题,必须用O(n log n)甚至O(n)。

输入输出方面,C++建议直接用:

#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // ...业务代码 return 0; }

Python则建议用:

import sys def main(): data = sys.stdin.read().split() # 然后用索引按顺序取数据 if __name__ == "__main__": main()

这种批量读入方式在数据量大的时候比input()一行行读快很多,能有效避免输入超时。

3.2 模拟题一:网格最小路径和(动态规划)

题目大概是这样的:给定一个 n x m 的矩阵,每个格子里有一个非负整数,从左上角出发走到右下角,每次只能向右或向下走,求路径上数字之和的最小值。数据范围 n, m 最大1000。

这道题是非常经典的DP题。状态定义:dp[i][j]表示从左上角走到(i, j)的最小路径和。转移方程:

dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])

边界条件是第一行只能从左往右走,第一列只能从上往下走。这个思路很直接,但如果只开一个二维数组,在 n = m = 1000 时空间是 10^6,完全没问题,但如果数据范围再大一点,就要考虑滚动数组优化。

这里我给出一个更稳的写法,直接用一维数组做空间压缩,同时在读入数据时实时计算,避免再开一个矩阵存储原始数据:

#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<long long> dp(m, LLONG_MAX); dp[0] = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { long long x; cin >> x; if (i == 0 && j == 0) { dp[j] = x; } else { if (j > 0) dp[j] = min(dp[j], dp[j - 1]) + x; // 如果 dp[j] 是上一行传下来的,不需要额外处理 } } } cout << dp[m - 1] << endl; return 0; }

严格来说这个写法是“边读边推”,省掉了存储矩阵的步骤。笔试时除非内存给得很小,否则直接用二维数组也不会有问题。但提交前你最好确认一下题目数据范围,如果 n 和 m 都到 10^5,那就不能用二维DP,得思考是不是有其他解法,比如把问题转化成最小割或者用贪心。实际上笔试很少出那么夸张的二维数据,多数情况下二维数组是能过的。

3.3 模拟题二:会议室安排(贪心 + 优先队列)

第二道题比较像面试里常考的“会议室II”。题意是:给出一组会议的开始时间和结束时间,问至少需要多少个会议室才能安排下所有会议。数据范围是 N 最大 10^5,开始和结束时间是整数。

这道题的正解是贪心加最小堆。先把所有会议按开始时间排序,然后遍历每场会议。用一个小顶堆维护“当前正在开的会议的结束时间”。如果堆顶的结束时间小于等于当前会议的开始时间,说明有会议室已经空闲,就把它弹出,让当前会议复用这个会议室;否则说明所有会议室都还在占用,只能新开一间。最后堆的大小就是最少需要的会议室数量。

代码:

#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<pair<int, int>> meetings(n); for (int i = 0; i < n; ++i) { cin >> meetings[i].first >> meetings[i].second; } sort(meetings.begin(), meetings.end()); priority_queue<int, vector<int>, greater<int>> pq; for (auto &[start, end] : meetings) { if (!pq.empty() && pq.top() <= start) { pq.pop(); } pq.push(end); } cout << pq.size() << endl; return 0; }

这道题我实际做的时候踩了一个小坑:如果两场会议的结束时间等于另一场的开始时间,是可以共用一个会议室的,所以判断条件是pq.top() <= start,注意是小于等于,不是小于。还有一点,数据范围给到10^5,说明要求 O(n log n) 解法,如果你看到堆就想到这个解法,基本就稳了。

3.4 编程题的提交与调试策略

编程题最怕的不是不会写,而是“思路对但没提交上去”。我总结了一套实战策略,这个策略在这次笔试里帮我稳住了节奏:

第一步,读完题先看数据范围,判断期望复杂度。n <= 20 大概率是状压DP或暴力搜索;n <= 1000 可以用 O(n^2);n <= 10^5 就要想 O(n log n)。这能帮你快速排除错误思路。

第二步,先写一个能跑的暴力版本拿部分分,尤其是第二题,暴力至少能拿到小数据用例的分。第一题如果思路清晰,直接写正解;如果卡住了,先写下暴力,再慢慢推导优化。

第三步,留出5到10分钟自测边界情况。包括:n = 1 的情况、所有元素相同的情况、最大值和最小值的情况。这些边界用例在赛后复盘中非常常见,但考场上很多同学因为时间紧张直接跳过,结果在看不到的测试数据上丢分。

4. 综合题与场景设计题:容易被忽略的“软实力”

4.1 场景设计题怎么答才能拿高分

小米笔试的简答/场景题不是简单的背诵题,它会给一个业务场景,让你完整设计一套算法方案。今年第二批的大致场景是:在一个短视频信息流产品里,面对新注册用户没有行为数据的冷启动问题,如何做内容的召回、排序和推荐策略。

这类题目没有标准答案,但阅卷人会看你的答题框架是否完整。我总结了一个比较通用的四段式结构:确定目标、拆解流程、选择方案、评估迭代。

先确定目标:冷启动用户推荐的短期目标是让用户尽快产生点击或互动行为,长期目标是留存和时长。所以需要明确评估指标,比如点击率、人均播放时长、次日留存。

接着拆解流程:推荐系统一般分召回和排序两个阶段。冷启动用户没有行为数据,所以不能依赖协同过滤,要考虑基于内容的召回,比如用户注册时选择的兴趣标签、手机品牌、年龄段、地理位置等基础画像信息,做规则召回和向量召回混合策略。

然后选择方案:冷启动推荐可以用聚类算法先对用户分群,比如把用户按注册设备、注册时段、画像标签聚成若干类,在新用户进入时匹配最近的用户群,然后推荐该群体热门或高转化的内容。排序阶段可以用XGBoost或者深度排序模型,但冷启动阶段特征稀疏,要加入更多语境特征,比如时间、位置、设备。如果追求实时性,还可以提到用多臂老虎机算法做探索与利用的平衡。

最后评估迭代:先小流量A/B测试,用一组老用户作为对照,观察新用户点击率、次留等指标是否显著提升,再决定是否全量上线。

这样答下来,至少在逻辑完整性上是及格的。要注意的是,不要只堆模型名字,要解释清楚“为什么这个环节选这个模型”,以及“数据量不够时怎么处理”。

4.2 简答题高频点:概念对比与原理推导

简答题部分,这次没考太偏的内容,核心还是几个经典问题,比如“XGBoost和GBDT的区别”“Batch Normalization为什么有效”“L1正则化为什么能产生稀疏解”。

答这类题的时间控制在15分钟内,尽量分条写。比如“BN为什么有效”,不要只写“可以加速训练、防止梯度消失”,要从以下几个角度展开:BN对每个mini-batch在通道维度上做归一化,让激活值保持在敏感区间;它削弱了网络中间层对初始参数尺度的依赖,这在一定程度上缓解了内部协变量偏移;BN还引入了两个可学习参数,保证网络可以恢复原始分布;此外BN还带了轻微的正则化效果,因为mini-batch的统计量有随机性。

如果遇到需要推导的题目,比如手写Softmax梯度,一定要把链式法则写清楚,分两步:先求损失对Softmax输入的梯度,再分析它与输出概率之间的关系。这类题目的关键不是计算多难,而是要让阅卷人看到你有推导能力。

4.3 笔试中的“时间管理学”

一道完整的场景设计题,从读题到写完大概需要15到20分钟。很多同学在选择题上花太多时间,导致后面编程题和简答题时间不够,这是笔试的大忌。

我自己的时间安排是:拿到试卷后先把所有题目扫一遍,尤其是两道编程题,看看难度差异,决定先做哪道。然后笔试选择题,控制在每题2分钟内。如果某道选择题超过3分钟还没思路,直接凭第一感觉选,并标记一下,即使时间富余也不会回头细想,因为选择题的容错率比较低,但编程题一题就是25分,性价比明显更高。

简答题放在编程题之后做,因为场景题再怎么写也就20分,编程题AC一道就是25分,先拿大头。如果你在编程题上卡死了,也要尽快跳到场景题写几句,确保不交白卷。说白了,笔试是“在有限时间内拿最多分”的考试,不是“每道题都完美”的考试。

5. 常见问题与避坑指南:这些坑我替你踩过了

5.1 笔试环境与工具准备清单

在线笔试最怕设备问题,但每年都有同学在设备上翻车。我建议在考试前按这个清单检查一遍:电脑电源要插上,不要只用电池;摄像头和麦克风权限要提前授权给浏览器;手机的二机位要充满电并设置为常亮;准备好身份证件;浏览器建议关掉所有无关插件和弹窗;网线比Wi-Fi更稳定,有条件就插网线。

还有一个细节:很多在线笔试平台会在考试开始时进行一次人脸识别,如果识别不通过,需要拍照上传等待人工审核,这个过程会占用考试时间。所以要提前把刘海梳起来,摘掉口罩,光线要充足,不然卡在这几步很浪费时间。

另外,本地IDE里写的代码要能直接运行调试。我建议用本地IDE把代码调试通过后再往网页编辑器里粘贴,但粘贴后一定要再运行一次用例,因为平台可能对代码做了一些处理,直接复制可能带入不可见字符,导致编译失败。

5.2 答题过程中的低级错误与典型案例

这里整理几个真实发生过的低级错误:

错误类型典型案例避免方法
数据范围判断失误矩阵乘法结果用int存,溢出变成负数看到累加、求和、乘法,直接用long long
边界条件遗漏合并区间时忘记处理空输入每个算法题都过一遍 n=0, n=1 的用例
输入输出格式错误输出多了空格或换行,被判格式错误严格按照题目说明输出,不要自作多情加提示语
堆栈溢出递归深度达到10^5导致栈溢出深度大的搜索改成迭代栈或循环
切屏被警告考试过程中切出去查看IDE插件考试前把所有工具准备好,全程不切屏

其中输入输出格式错误是最亏的,代码逻辑全对,结果因为多打印了一个“请输入n:”被判0分,这属于需要坚决避免的失误。

5.3 笔试评分视角与后续面试衔接

笔试结束后我有一次机会和一位参加面试的同学沟通,了解到小米的面试官在面试时是会看到笔试记录的,包括编程题的提交代码和得分。这意味着笔试代码的风格,比如变量命名是否清晰、注释是否简洁、有没有明显的边界漏洞,都会被面试官看到。

所以笔试过程中不要只追求“调试通过就提交”,要尽量写出有可读性的代码。哪怕是暴力解,也要把变量名写清楚,把关键逻辑用一行注释说明。面试官看你的笔试代码时,判断的不仅是你会不会这道题,更是你在真实工程中写代码的规范性。

笔试复盘也很有价值。考试结束后我把自己错的选择题和卡住的编程题重新整理了一遍,发现大部分错误都来自对边界条件的忽视和对数据范围不够敏感。如果没有这一轮复盘,后续面试里被问到类似问题时,我可能还会在同一个地方栽跟头。

5.4 给下一届考生的特别建议

最后再针对准备参加算法岗秋招的同学说几句掏心窝的话。不要只刷力扣的hot 100,笔试选择题覆盖的知识面要比单纯刷题广很多,尤其是机器学习和深度学习理论,建议把西瓜书或同等教材的课后题过一遍。编程题要尽早适应ACM模式,刷力扣时可以用自定义输入输出来练习,因为很多人写核心函数没问题,一让处理输入输出就手忙脚乱。

还有就是,平时刷题要养成“先看数据范围再想算法”的习惯。很多时候笔试的题目难点不在于算法的名字有多高级,而在于能否从数据范围反推正确的时间复杂度。这个习惯不是一朝一夕能养成的,但一旦养成,笔试答题会顺手很多。

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

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

立即咨询