小米算法岗第一批笔试备考攻略:题型拆解与高频算法实战
2026/9/1 10:34:02 网站建设 项目流程

今年秋招算法岗的情况大家多少都有体感,前几年那种“海投简历就有面试”的好日子基本过去了,笔试环节成了第一道硬门槛。我身边不少朋友在准备小米集团算法岗的时候,第一个碰到的就是“第一批笔试”。这名字听起来平平无奇,但里面信息量其实不小:它意味着卷子不是往年固定题库,而是按批次滚动出题,题目质量和难度也会随批次浮动。

这篇文章不打算灌鸡汤,也不搞什么“X天速通大厂笔试”的玄学。我想从一个实际参加过、也陪跑过几轮校招的视角,把小米算法岗第一批笔试这件事拆开聊:题型结构大概长什么样,编程题怎么审题、怎么写能多拿分,哪些算法是高频考点,以及那些笔试时容易踩的坑。不管你是刚刷完力扣准备试水,还是已经被几场笔试虐过想针对性补强,这篇文章应该都能给你一些能直接用的东西。

1. 先聊聊小米算法岗笔试到底考什么

1.1 “第一批笔试”是什么,批次差异有多大

小米的秋招一般会分好几个批次滚动安排,第一批笔试通常挂在简历投递截止后的两周内。很多同学会纠结“第一批是不是更难”或者“第一批是不是更简单”,我的看法是:这个问题没有标准答案,但第一批有一个特殊的地方——它往往是题库更新后的第一批卷子,题目风格会直接影响后面几个批次的走向。

从实际操作看,小米的笔试系统和很多大厂一样,用的是牛客或者赛码这类在线笔试平台,题目从题库中随机抽。这意味着同一批次的同学拿到的题可能不完全一样,但题型结构和难度区间是大致对齐的。所以“第一批笔试”更像是一个时间节点概念,而不是难度分层概念。与其纠结批次,不如把精力放在摸清出题风格上。

1.2 算法岗位不只是考算法

有个误区得先说清楚:算法岗笔试不一定只考纯算法。小米的算法岗方向很宽,有做 NLP 的、有做 CV 的、有做搜广推的,也有做机器学习平台的。不同方向的面试官关注点不同,但笔试环节通常是统一出题,考察的还是“通用算法能力 + 数据结构基础 + 机器学习基础”这个三角。

我印象里这类笔试的选择题部分,经常会出现统计概率、机器学习基础概念,比如过拟合的处理方式、正则化的作用、常见损失函数的适用场景等。编程题则以数据结构和经典算法为主,很少出现需要依赖某个特定框架或深度模型的题目。这个布局其实挺合理:笔试环节考察的是“你作为算法工程师的基本盘稳不稳”,而不是“你会不会调某个库”。

1.3 难度定位:比周赛简单,比 Easy 略难

如果非要给个参考系,我个人体感是:小米算法岗笔试的编程题难度介于 LeetCode 的 Easy 和 Medium 之间,个别题会摸到 Medium 中上水平,但很少出现 LeetCode Hard 级别的压轴题。牛客周赛的 T1、T2 难度差不多能覆盖大半。

不过别高兴太早,“难度不高”和“分数高”是两码事。笔试评分往往按通过率精确到小数,有些题看似简单,但边界条件一多,很容易掉进细节陷阱。比如数组越界、空输入、整数溢出,这些都是实打实的失分点。后面我会专门展开讲。

2. 题型结构与考点分布拆解

2.1 选择题:数据结构与机器学习基础五五开

选择题通常是 15-20 道,每道题分值不大,但架不住量大。从过来人的经验看,高频考点集中在几块:

  • 数据结构:KMP 算法的 next 数组计算、排序算法的稳定性和时间复杂度、堆的建堆过程与复杂度、二叉树的遍历序列还原、哈希冲突的解决方法。
  • 算法设计:贪心算法的适用场景、动态规划的 state 设计、二分查找的边界写法、Dijkstra 与 Floyd 的适用条件。
  • 机器学习:过拟合与正则化、偏差方差分解、常见评价指标(准确率、召回率、F1、AUC)、梯度下降变体的区别、交叉验证的作用。

这里特别提醒一句:KMP 的 next 数组是选择题的常客,而且经常不是让你写代码,而是给你一个具体的模式串,让你直接写出 next 数组或失配后的跳转位置。这种题很多人平时写代码是“背模板”,一落到手算就懵,后面我会拿具体例子演示一遍。

2.2 编程题:场景包装下的经典模型

编程题一般是 2-3 道,每道题 20-30 分。小米的出题风格有个特点:喜欢给题目套一个业务场景的外壳,但剥开之后核心还是经典模型。

举个例子,某道题表面是说“工厂流水线上 N 个任务,每个任务有开始时间和结束时间,安排最优调度”,本质上就是区间调度问题,贪心一排序就能做。还有的题说“给一串订单,按价格和下单时间排序”,其实就是多关键字排序。这种包装本身不可怕,可怕的是你在考场上被场景带跑了,没抽出背后的数学模型。

我的建议是:读题时拿笔把题目里的数字、范围、约束条件圈出来,然后马上问自己三个问题——这个需求对应什么数据结构?什么算法模型?有没有经典的板子能直接套?

2.3 高频考点的优先级排序

为了让大家准备的时候有个轻重缓急,我根据自己的经验列了个优先级表格,按“出现频率 × 投入产出比”排的:

优先级知识点常见考查方式
必考数据结构(数组、链表、栈、队列、哈希)选择题 + 编程题底层支撑
必考排序算法复杂度与稳定性选择题高频
高频二分答案 / 二分查找编程题高频
高频贪心算法编程题高频,结合排序
高频动态规划(一维 / 二维背包 / 序列DP)编程题中高频
中频图论(BFS / DFS / 最短路径)编程题中频
中频字符串(KMP 手算、前缀哈希)选择题中频,编程题低频
中频树(遍历、最近公共祖先)选择题 + 编程题低频
低频数论(快速幂、素数筛)选择题低频,编程题偶见
低顺位复杂算法(线段树、后缀数组、网络流)基本不考,少见但非绝对

提示:这个优先级不是绝对的,比如个别批次可能突然冒出一道偏门的数论题。但从“投入产出比”角度讲,线代排序动态规划这几个方向是性价比最高的,先把必考和高频吃透,比盲目刷难题划算得多。

3. 编程题的核心套路与临场解题流程

3.1 拿到题先别急着敲代码

很多同学一上考场就紧张,看到题目开头有一大段场景描述,直接跳过去找输入输出格式,然后稀里糊涂开始写。这个习惯非常危险。笔试的编程题藏分点往往就在场景描述里,比如“任务可以并发执行”和“任务串行执行”就是两种完全不同的解法,前者可能是贪心 + 堆,后者可能只是简单累加。

我的习惯是:读题读两遍,第一遍通读,搞明白题目在说什么;第二遍精读,把输入范围、边界条件、输出要求圈出来。尤其是数据范围,它直接决定你敢不敢用 O(n^2) 的解法。如果 n ≤ 10^5,O(n^2) 大概率超时,得想 O(n log n) 或 O(n) 的解法;如果 n ≤ 500,那 O(n^3) 都可能能跑过,暴力枚举先拿分再说。

3.2 第一目标:先拿暴力分,再谈优化

笔试和面试不一样,面试你可以跟面试官讨论思路,笔试只看最终代码的通过率。所以一个非常实用且我反复验证过的策略是:如果一时半会想不出最优解,先写暴力,保证拿到部分分数。

举个例子,一道题要求计算“数组中所有连续子数组的和”,最优解是前缀和 O(n),但如果你第一时间没想到,可以先写两层循环枚举所有起点和终点。等这道题暴力版本写完了,你大概率在写的过程中会发现重复计算的部分,这时候再优化成前缀和就顺理成章了。暴力代码不是白写,它是你的保底分,也是你思考的跳板。

3.3 将题目映射到算法模型的信号词

做题时间久了,会发现每类算法都有一些典型的“信号词”。我把它们列出来,遇到这些词可以直接触发对应的算法板子:

  • “最大/最小化某个值” + 数据范围较大:优先考虑二分答案。
  • “最多能完成多少/最少需要几次”:可能是贪心或动态规划,需要结合数据范围判断。
  • “是否存在……路径/能不能到达”:BFS 或 DFS,图论题。
  • “有多少种组合/方案数”:动态规划概率很大。
  • “按某个条件排序后处理”:排序 + 双指针或排序 + 贪心。
  • “相邻两个……之间的关系”:栈、队列或 DP。
  • “x 的 y 次方 / 取模的大数幂”:快速幂。

这套映射不是万能公式,但能在几分钟内帮你圈定思考范围,避免在错误方向上耗太久。

3.4 写代码时的几个细节习惯

考场上很多问题不是“思路不会”,而是“代码细节没处理好”。我总结几个踩过坑的地方:

  • 用 long long 别用 int。凡是涉及累加、乘法、下标计算,不确定范围的尽量用 64 位整数,笔试平台常见溢出错误大多源于 int 溢出。
  • 处理好空输入和极端数据。链表为空、数组长度为 1、全是负数的用例,写代码前先在草稿纸上推一遍。
  • 输出格式严格对照题面。多了一个空格、少了一个换行,都可能被判格式错误,部分平台直接算 0 分。
  • 提交前至少自测 3 个用例:题干给的样例、一个最小规模用例、一个极端用例都要跑。

4. 高频算法专项拆解:从原理到现场快速推导

4.1 KMP 与 next 数组:别再背模板了

KMP 算法是选择题里的“钉子户”,但它很多年都不直接考代码实现,而是考 next 数组的手算。就拿一个典型的模式串 p = "abacaba" 来说,next 数组怎么推导?

先明确一种常见定义:next[i] 表示模式串前 i 个字符组成的子串中,最长相等前后缀的长度(不包含子串自身)。逐个位置看:

  • i=0,子串 "a",没有真前后缀,next[0]=0。
  • i=1,子串 "ab",前缀 {"a"},后缀 {"b"},无交集,next[1]=0。
  • i=2,子串 "aba",前缀 {"a","ab"},后缀 {"a","ba"},最长相等前后缀是 "a",next[2]=1。
  • i=3,子串 "abac",前缀有 "a","ab","aba",后缀有 "c","ac","bac",无相等,next[3]=0。
  • i=4,子串 "abaca",前缀 "a","ab","aba","abac",后缀 "a","ca","aca","baca",最长相等前后缀是 "a",next[4]=1。
  • i=5,子串 "abacab",前缀到 "abaca",后缀有 "b","ab","cab","acab","bacab",最长相等前后缀是 "ab",next[5]=2。
  • i=6,子串 "abacaba",前缀到 "abacab",后缀有 "a","ba","aba","caba","acaba","bacaba",最长相等前后缀是 "aba",next[6]=3。

最终 next 数组是 [0, 0, 1, 0, 1, 2, 3]。理解这个推导过程比死记代码模板重要得多。考场上一旦给了别的模式串,理解了原理就能随手推出来,而背模板只能祈祷它考的原题。

4.2 快速幂:一道题拿下位运算和取模

热词里频繁出现“快速幂算法”,这个考点在笔试里确实偶有出现,而且经常藏在“计算 a 的 b 次方对 p 取模的结果”这类题后面。

先看朴素的循环乘法:O(b) 的时间复杂度,如果 b 是 10^9 级别,必然超时。快速幂的核心思想是把指数 b 拆成二进制,比如 a^13 可以拆成 a^8 × a^4 × a^1,因为 13 = 1101₂。每次迭代把底数平方,同时右移指数,遇到当前二进制位为 1 就把累积结果乘上当前的底数。这就是 O(log b)。

C++ 的参考实现:

long long fast_pow(long long a, long long b, long long mod) { long long res = 1 % mod; a %= mod; while (b > 0) { if (b & 1) { res = res * a % mod; } a = a * a % mod; b >>= 1; } return res; }

有几个细节要注意:res初始化为1 % mod是为了处理 mod=1 的极端情况;a先取模是为了防止初始值过大溢出;每次乘法后都取模,保证中间结果始终可控。这些细节就是笔试里“都是思路,为啥分数不一样”的差距所在。

4.3 排序与堆:会写 API 也得知道底层

排序算法的选择题考点主要是复杂度和稳定性。有些同学用惯了std::sort,问起来却说不出快排的最坏时间复杂度,这不行。

准备一个速记表:

排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性
冒泡排序O(n^2)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(1)不稳定
插入排序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+k)O(n+k)O(k)稳定

顺带说一下建堆的时间复杂度这个高频坑。很多人以为建堆是 O(n log n),但实际上是 O(n)。原因是从最后一个非叶子节点往上做向下调整,大部分节点只需要常数次比较,整体代价摊还后是线性。选择题里如果问“构建一个 n 个元素的最小堆时间复杂度”,答案是 O(n),不是 O(n log n)。

TopK 问题也是编程题的老面孔。找前 K 大的数,经典方案是用大小为 K 的小顶堆,堆顶就是当前第 K 大的门槛,遍历一遍数组,遇到比堆顶大的就替换并调整堆,时间复杂度 O(n log K)。如果 K 远小于 n,这个方案比全局排序快得多,也是面试官喜欢的思路。

4.4 区间贪心:一个模型吃透一类题

热词里有一类“雷达覆盖”相关的题,这类题剥开壳子就是区间问题的最经典模型:给定若干区间,选择最少个数的点,使得每个区间都至少包含一个选中的点。

贪心策略很简单:按区间右端点从小到大排序,然后维护一个last变量表示当前选中的最右端点。遍历区间,如果当前区间的左端点大于last,说明这个区间没被覆盖,需要新增一个点,并更新last为当前区间的右端点;否则就继续复用之前的点。

C++ 的核心写法:

struct Interval { double left, right; }; vector<Interval> intervals; // 按右端点排序 sort(intervals.begin(), intervals.end(), [](const Interval& a, const Interval& b) { return a.right < b.right; }); int count = 0; double last = -INFINITY; for (const auto& seg : intervals) { if (seg.left > last) { count++; last = seg.right; } }

这个模型的变体很多:会议室安排、任务调度、灌溉范围覆盖等。关键点在于贪心选择的合理性证明——每次选在当前最右侧位置放置点,能给后续区间留下最大空间,这就是局部最优能导出全局最优的原因。考场上不要求严格证明,但心里要清楚为什么这么排序。

4.5 二分查找:两个板子解决边界恐惧

二分查找的代码实现细节是笔试选择题和编程题的双重高发区。闭区间和左闭右开区间是两套主流写法,混着用容易越界或死循环。我建议只练一种:左闭右闭

// 在 [l, r] 区间内查找第一个 >= target 的位置(lower_bound) int lowerBound(vector<int>& nums, int target) { int l = 0, r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] >= target) { r = mid - 1; } else { l = mid + 1; } } return l; } // 查找第一个 > target 的位置(upper_bound) int upperBound(vector<int>& nums, int target) { int l = 0, r = nums.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; if (nums[mid] > target) { r = mid - 1; } else { l = mid + 1; } } return l; }

记住这套模板的核心:当nums[mid] >= target时压缩右边界,当nums[mid] < target时压缩左边界。最终l指向的就是答案位置。这样不管题目问的是“找到 target 的第一个位置”还是“插入位置”,都能直接套。mid 用l + (r - l) / 2而不是(l + r) / 2,是为了防止 l + r 整型溢出。

4.6 动态规划:拿到题先做这三步

动态规划在算法岗笔试里的出场率很高,一维 DP、二维 DP、背包类 DP 都常出现。我拿到一个疑似 DP 的题,喜欢按三步走:

第一步,定义状态。一维 DP 时想清楚 dp[i] 代表“以第 i 个元素结尾的最优值”还是“前 i 个元素的最优值”,这两者有本质区别。第二步,找转移方程。把第 i 个状态跟前几个状态联系起来,这一步考验的是对子结构关系的理解。第三步,初始化与遍历顺序。dp[0] 或 dp[1] 给多少,外层循环从哪开始,内层循环方向是正还是倒,这些细节直接决定代码能不能跑对。

比如经典的最长回文子串问题,dp[i][j] 表示第 i 到第 j 个字符是否是回文,转移方程是dp[i][j] = (s[i] == s[j]) && (j - i <= 2 || dp[i + 1][j - 1])。这种情况下遍历顺序就不能单纯从 0 到 n,而要按子串长度从短到长,否则用到的 dp[i+1][j-1] 还没算出来。这种细节就是笔试里真正拉开差距的地方。

5. 常见问题与排查技巧实录

5.1 时间分配:选择题别恋战

80-100 分钟的笔试,选择题加编程题各占一半时间通常是合理的。但很多同学会在选择题上死抠一道 KMP 手算题,算了一遍觉得不对又算一遍,十分钟没了。

我的建议是:选择题平均每道控制在 1 分半到 2 分钟,超过 3 分钟还没思路就先标记跳过。编程题留足至少 50 分钟,因为写代码、调试、自测都需要时间。如果最后编程题卡住了,再回头补跳过的选择题也不迟。记住一个原则:选择题分值再高也是单个选项,编程题一跑通就是大几十分,投入产出比完全不在一个量级。

5.2 边界条件失守:最容易丢分也最好拿回

边界条件失守是编程题最常见的丢分原因,但它恰恰是准备成本最低的提分点。每次写完代码,花 30 秒自查一遍:

  • 输入为空时,你的代码会返回什么?会不会越界?
  • n=1 时,循环和递归能正常处理吗?
  • 数组下标有没有可能出现 -1 或 n?
  • 累加结果会不会超过 int 范围?
  • 题目要求输出浮点数时,精度格式对不对?

这五个问题你要是每次提交前都过一遍,编程题至少能挽回 5%-10% 的通过率。不要觉得这是小事,校招笔试的分数分布非常密集,几分之差就可能影响后续流程排序。

5.3 笔试做题顺序:先易后难是铁律

考场上建议按照“一眼会的编程题 → 选择填空 → 不卡壳的编程题 → 难啃的编程题”的顺序进行。先做会做的题,既能快速稳住心态,也能确保基础分落袋。碰到一道题看了 10 分钟还没有完整思路,先干别的事,让潜意识再跑一会,往往等回头再看的时侯就茅塞顿开了。

另外,编程题如果实在没思路,裸写暴力也有意义。有些平台会按通过的测试点比例给分,暴力能过一部分用例就是一部分,别轻易放弃。

5.4 批次与后续面试的衔接

还有一个容易被忽略的点:小米这种批次化笔试,出分之后通常紧接着就是面试筛选,笔试题目的复盘材料可能直接被面试官拿来追问。比如笔试考了动态规划,面试官可能问“那道题你的状态定义是什么?为什么这么定义?还能怎么优化?”所以笔试结束后不要立刻忘掉题目,最好趁还有记忆把每道题的思路和代码整理下来,这既是复盘,也是为面试准备素材。

我自己的习惯是笔试后 24 小时内写一篇简短的复盘,记录题目类型、我的解法、优化方案、卡住的原因。这个习惯看起来麻烦,但到了面试阶段非常有用——能说出清晰的思考过程和优化路径的候选人,跟只写过代码的候选人,完全是两个印象。

6. 最后:一点值得分享的个人体会

笔试准备说到底不是看谁刷的题多,而是看谁能把核心算法模型吃透、把细节控到位。小米这类大厂的算法岗笔试,考察思路比较正统,常规考点占比高,偏题怪题很少。这意味着只要把数据结构、排序、二分、贪心、动态规划、基础图论这几块练扎实,笔试通过并不是遥不可及的事。

如果让我给准备 2025 届及以后批次的同学一个建议,我会说:把重点放在“理解算法原理 + 熟练模板 + 高频自测边界”上,而不是去追求那些花哨的冷门算法。KMP 能手推 next 数组吗?快速幂能默写吗?二分的两个板子能闭眼写完吗?这些问题如果都能快速给出肯定的答案,那你面对的笔试题目大概率就已经拿下一大半了。

最后再分享一个小技巧:练习的时候多用纸笔手算算法过程,别太依赖 IDE 的调试器。笔试环境里调试功能通常很弱,而且选择题里的手算题根本不允许你调代码。每次刷题时,先在大脑里把输入、状态变化、输出完整过一遍,再动手敲键盘,这个过程练得越多,考场上就越稳。

祝接下来笔试的各位都能顺利过关,拿到自己心仪的 offer。有问题也欢迎在评论区交流,我尽量抽空回复。

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

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

立即咨询