小米秋招软件开发笔试复盘:基础考点、编程题思路与备考策略
2026/9/1 22:19:41 网站建设 项目流程

9月下旬的某天晚上,我卡着点进了2021年小米秋招软件开发方向第二场笔试的在线考场。我之所以把这套题复盘专门写出来,是因为它几乎是国内互联网大厂和硬件厂软件开发笔试的一个标准缩影:选择题覆盖面广但不算偏,编程题难度梯度拉得比较开,整体非常考验“基础功是否扎实”和“能不能在有限时间内做出取舍”。当时和我一起投递软件开发方向的同学,有不少人挂在编程题上,不是不会写,而是前面选择题磨太久,到后面没时间调通代码,非常可惜。

这篇文章不会只粘贴题目答案,而是把整场笔试拆开揉碎,讲清楚它到底在考什么、每一类题目背后的知识点是什么、编程题的完整解题思路和代码怎么写,以及复盘之后我总结出的备考路线。如果你正在准备大厂软件开发岗的笔试,或者还在犹豫要不要刷题、刷什么题,这篇内容值得你花20分钟看完,至少能帮你少踩几个我踩过的大坑。

1. 笔试整体结构与考察方向拆解

1.1 题型分布与时间压力

当时我拿到卷子后的第一感觉是:题量不算变态,但时间依然紧张。整套笔试大致分成两个大块,一是计算机基础类选择题,二是算法编程题,部分场次还会穿插一两道系统设计或简答题。我记得整场笔试时长是120分钟,选择题大概有30道左右,编程题2到3道,具体数量会随批次略有浮动。

别小看这30道选择题,它们的考察范围基本覆盖了计算机专业的核心课程:数据结构、算法、操作系统、计算机网络、数据库、计算机组成原理,偶尔还会冒出一两道C++内存布局、Linux命令、设计模式相关的题。单道题的分值虽然不高,但错太多直接影响笔试总评,毕竟大厂笔试通常会设一道基础题正确率门槛,基础题大面积失分的话,编程题全对也可能被卡掉。

时间压力主要来自两个方面。第一是选择题需要“快速判断”,很多题不是算不出来,而是算得慢,比如考察进程调度算法、IP地址子网划分、数据库索引结构相关的计算题,每道题控制在90秒内完成才算安全。第二是编程题需要“一次写对”,在线笔试不像本地IDE那么顺手,无法调试太久,基本上第一道编程题要在25分钟内过掉,第二道题才有充足时间优化和验证。

1.2 科目权重说明了什么

从我收集到的信息和我自己那场的感受来看,小米软件开发方向的笔试更偏重“工程基础”而不是“竞赛算法”。选择题里数据结构、操作系统、网络这三块占比最高,其次是数据库和计组,算法题难度则介于LeetCode中等题和困难题之间,极少出现那种需要灵光一现的偏难怪题。

这个权重分配其实和硬件大厂软件岗的日常工作是吻合的:你做嵌入式、驱动、中间件、系统开发,天天要和内存、进程、网络、并发打交道,这些基础科目就是吃饭的家伙。算法题考得不会太偏,但也绝不是随便背几个模板就能应付的,它更倾向于考“你会不会把数据结构和算法思想用到实际场景里”,比如数组处理、动态规划、图的最短路径都是高频考点。

所以,如果你现在还在纠结“要不要把《算法导论》完整啃一遍”,我的建议是:不要。大厂校招笔试不会考论文级别的算法证明,你需要的是把常见数据结构的操作复杂度背熟,把高频算法题的套路练熟,把操作系统和网络的核心概念搞透,这些才是性价比最高的复习方向。

2. 计算机基础选择题的高频考点与避坑思路

2.1 数据结构与算法类选择题怎么秒判

数据结构的选择题一般集中在几个固定的点上:栈和队列的应用场景、二叉树遍历和性质、排序算法的稳定性和复杂度、哈希表冲突处理、图的最小生成树与最短路径。这些题说穿了就是“背结论 + 会推导”,但有三个我反复踩坑的地方,特别值得拿出来说。

第一个是排序算法的稳定性判断。快速排序、堆排序、选择排序不稳定,冒泡、插入、归并、基数排序稳定,这个一定要记牢,因为笔试不会直接问“哪个稳定”,而是会换成“下列排序算法中,哪些在平均情况下时间复杂度为O(n log n)且是稳定的”,这种组合式考法特别容易让人犹豫。我就是在这种题上吃过亏,当时选成了快速排序,结果漏了归并排序,白白丢分。

第二个是二叉树相关性质的快速计算。n0 = n2 + 1这个公式要变成条件反射,满二叉树和完全二叉树的节点编号、层数换算也必须熟练。还有一点是赫夫曼树的构造,考试不会给你特别复杂的节点,但WPL(带权路径长度)务必亲手算一遍,因为几个选项里的数值通常设计得非常接近,心算出错的概率很高。

第三个是哈希表。考察点主要是哈希函数构造、冲突处理方法(开放定址、链地址法)以及查找成功/失败时的平均查找长度计算。这类题没有捷径,必须老老实实画表模拟。我建议在草稿纸上画一个清晰的表格,列清楚每个关键字计算出的哈希地址、冲突次数,避免重复计算。

2.2 操作系统、网络、数据库与计组的经典题型

操作系统选择题的高频区域几乎可以提前锁定:进程与线程区别、进程状态转换、调度算法(先来先服务、短作业优先、时间片轮转、多级反馈队列)、死锁产生的四个必要条件与银行家算法、虚拟内存与页面置换算法(OPT、FIFO、LRU、Clock)、进程同步与PV操作。这里我要特别提醒一点:银行家算法和页面置换是笔试最爱出大题式选择题的,一定要把完整的判断流程写熟练,因为这类题计算量不小,如果没有提前练过,当场推演很容易节奏崩掉。

计算机网络部分主要围绕OSI七层模型和TCP/IP四层模型展开,高频考点包括:各层协议与设备的对应关系、TCP三次握手和四次挥手的状态迁移、TCP与UDP的区别、滑动窗口与拥塞控制(慢开始、拥塞避免、快重传、快恢复)、IP地址分类与子网划分、DNS解析过程、HTTP状态码含义。子网划分几乎是必考项,遇到这类题一定要先把IP地址转成二进制,再算网络号、广播地址和可用主机数,不要凭感觉口算,很容易在边界条件上栽跟头。

数据库的选择题相对友好一些,核心就是关系代数与SQL、范式判断与规范化、索引底层结构(B+树)、事务ACID特性与隔离级别、并发控制(锁、MVCC)。范式的判断题是高频易错点,我建议把1NF到BCNF的定义用“函数依赖”的角度重新理解,不要死记硬背,只要你能判断出“非主属性对码的部分函数依赖”和“传递函数依赖”,大部分题目都能应付。

计算机组成原理部分主要考数据的表示与运算(原码、反码、补码、移码)、IEEE 754浮点数格式、存储层次(Cache、主存、虚存)、指令流水线、中断与DMA。补码的加减法和溢出判断是高频基础题,Cache的命中率与平均访问时间计算也经常出现。这类题需要一点数字敏感度,建议考前把所有计算公式列成一张表,每天过一遍。

3. 算法编程题复盘:两道典型题目的完整拆解

3.1 第一题:子数组相关问题的优化套路

我印象里那场的编程题第一道是给定一个整数数组和一个目标值k,要求找出和为k的最长连续子数组的长度,不存在则返回0。这道题在LeetCode上有原题,属于“前缀和 + 哈希表”的经典应用,难度不高,但非常考验你的优化意识。

最简单的暴力解法是两层循环枚举左右边界,时间复杂度O(n^2),对于 n = 10^5 的数据范围一定会超时。核心优化思路是:区间 [j, i] 的和等于 prefixSum[i] - prefixSum[j-1],如果它等于k,那说明 prefixSum[j-1] = prefixSum[i] - k。所以我们只需要遍历数组一遍,用哈希表记录某个前缀和第一次出现的位置,然后在当前 i 位置寻找 prefixSum[i] - k 是否在哈希表里,如果在,就用 i - firstIndex 更新答案。

这里有一个非常关键的细节:哈希表里存的必须是某个前缀和“第一次出现的位置”,而不是最后一次出现的位置,因为我们要找的是最长子数组。我第一次写这道题的时候,不小心在哈希表里覆盖了旧索引,导致求出来的答案要么偏短,要么直接算错。这也是线上线下笔试很常见的失分点,因为样例数据往往很弱,跑一遍要么对要么错,但如果是部分正确,平台不会告诉你哪个用例挂了,只能自己干瞪眼。

C++参考实现如下:

#include <bits/stdc++.h> using namespace std; int longestSubarraySumK(vector<int>& nums, int k) { unordered_map<int, int> firstPos; firstPos[0] = -1; // 前缀和为0的位置设为-1,方便计算从0开始的子数组 int prefix = 0; int ans = 0; for (int i = 0; i < nums.size(); i++) { prefix += nums[i]; if (firstPos.count(prefix - k)) { ans = max(ans, i - firstPos[prefix - k]); } if (!firstPos.count(prefix)) { firstPos[prefix] = i; } } return ans; } int main() { int n, k; cin >> n >> k; vector<int> nums(n); for (int i = 0; i < n; i++) cin >> nums[i]; cout << longestSubarraySumK(nums, k) << endl; return 0; }

为什么要初始化为 firstPos[0] = -1?因为如果整个数组从0到 i 的和刚好等于k,那么我们需要让 i - (-1) = i + 1,这样才能计算出完整长度。这个边界处理是这道题最容易出错的地方。

3.2 第二题:动态规划状态设计的核心思路

第二道编程题我记得是一道关于任务调度的题目,场景大概是:每个任务有一个持续时间和一个前置任务约束,如果某个任务有前置任务,必须等前置任务完成后才能开始,假设当前系统有多个工作线程并行处理任务,问完成所有任务的最短时间是多少。这种题本质上是带依赖关系的调度问题,需要把“并行执行”和“依赖顺序”组合起来考虑。

先说怎么建模。前置约束可以构成一个DAG(有向无环图),对于每个节点,定义 dp[i] 为“完成节点 i 及其所有前置任务所需的最短时间”。因为多个任务可以并行执行,所以完成一个任务的最短时间其实是“所有前置任务完成时间中的最大值 + 当前任务的持续时间”。用公式表示就是:

dp[i] = max(dp[prev]) + cost[i]

这里必须取最大值而不能取和,因为前置任务可以并行执行,所有前置任务里最后完成的那一个决定了当前任务最早可以开始的时间。理解这一点是解出这道题的关键。

实现上先用拓扑排序遍历所有节点,一边遍历一边更新每个节点的 dp 值,最后所有节点 dp 的最大值就是答案。代码框架大概长这样:

from collections import deque def solve(n, costs, prerequisites): graph = [[] for _ in range(n)] indeg = [0] * n for a, b in prerequisites: # b必须在a之前完成 graph[a].append(b) indeg[b] += 1 q = deque() dp = [0] * n for i in range(n): if indeg[i] == 0: q.append(i) dp[i] = costs[i] ans = 0 while q: u = q.popleft() ans = max(ans, dp[u]) for v in graph[u]: indeg[v] -= 1 dp[v] = max(dp[v], dp[u] + costs[v]) if indeg[v] == 0: q.append(v) return ans

为什么用 dp[v] = max(dp[v], dp[u] + costs[v]) 而不是直接赋值?因为一个节点的前置任务可能有很多个,每个前置任务完成的时间不同,必须取最晚的那个作为真正的开始时间基础。这个“取最大”和“累加”的区别,就是动态规划状态转移中的核心辨析点,笔试如果在这里写错,样例过不去,排查起来还不好找。

3.3 在线编程的输入输出与调试技巧

不少同学在本地IDE里写代码溜得很,一上在线笔试平台就卡在输入输出上。软件开发方向的笔试通常要求自己处理输入,尤其是C++的cin/cout和Java的Scanner。我当时的建议是提前把模板写熟,比如C++用ios::sync_with_stdio(false); cin.tie(nullptr); 加速输入输出,Python用sys.stdin.read()一次性读取然后split,避免逐行读导致超时。

还有一个容易翻车的点是:在线笔试平台不允许查看所有测试用例,很多用例设计得非常刁钻。比如数组长度为1、目标值等于数组唯一元素、所有元素为负数、数据范围接近int上限等。我建议每道题写完核心逻辑后,花两分钟从头想一遍边界条件:空数组、单个元素、重复元素、无解情况、数值溢出情况。这比检查语法重要得多,因为在线判题只关心你的代码能不能通过所有隐藏用例。

调试方面,由于平台没有断点调试功能,我的习惯是先用小规模数据在纸上推演一遍逻辑,然后直接在代码里加打印日志查看关键变量的中间值,确认无误后再删掉打印语句。千万不要一上来就凭感觉提交,尤其是第二道编程题,一旦提交失败,重新编译、重新看题、重新排查的时间成本极高。

4. 软件设计与方案题的作答思路

4.1 这类题目到底在考什么

小米软件开发笔试有时会夹带一两道“简答题”或“设计题”,比如让你设计一个短链接系统、设计一个线程池、设计一个缓存淘汰策略、说明你对某个开源项目的理解。这类题目单看分值不高,但它是面试官快速判断你“工程思维”是否到位的重要参考,最好不要太潦草。

设计题的核心考点不是“标准答案”,而是你有没有一套完整的设计框架。以“设计一个线程池”为例,一个合格的回答至少应该涵盖:线程池的核心参数(核心线程数、最大线程数、任务队列、拒绝策略)、任务提交和执行流程、线程池状态管理、如何优雅关闭。如果你能把每个参数设置的“为什么”也讲清楚,比如为什么核心线程数和最大线程数不能拍脑袋定,而要结合任务类型是CPU密集型还是IO密集型来考虑,这个回答就很容易拿高分。

4.2 如何在有限时间写出高完成度方案

在线笔试时间宝贵,设计题不需要写出一篇论文,我建议按照“需求分析 → 架构设计 → 核心模块设计 → 关键接口/伪代码 → 潜在问题与优化方向”这个顺序作答,每个部分控制在3到5行,把核心思路讲清楚即可。

一个非常好用的策略是画ASCII流程图或者写伪代码。在线笔试系统通常支持纯文本作答,你可以在答案里用缩进和箭头把流程串起来。比如设计一个缓存系统,可以写“请求到达 → 查缓存 → 命中直接返回 → 未命中查数据库 → 回写缓存 → 设置过期时间 → 返回结果”。这种表达比大段文字更直观,阅卷人一眼就能看出你的思路是否清晰。

另外,设计题千万别只写“用什么技术栈”,如果题目没有特别要求,不要堆砌“用Spring Boot + Redis + MySQL”这种话,而要把重点放在为什么这样设计、容量评估怎么做、存在哪些瓶颈、如何扩展这些实际问题上。我当时做题时看到不少同学的设计题基本就是在写功能清单,没有任何数据量评估和方案对比,这种答案很难打动阅卷人。

5. 复盘后的避坑清单与备赛建议

5.1 考场上最容易犯的五个错误

第一,选择题时间分配失控。这是最普遍的问题。我见过有的同学在选择题上花了近70分钟,最后编程题只剩20分钟,基本只能交白卷。我的建议是选择题整体控制在一小时以内,每道题一旦超过2分钟还没思路就先标记跳过,全部做完后再回来看,不要让一道难题毁掉两道必得分的编程题。

第二,编程题不写“思考过程”直接上手。很多在线笔试平台支持提交备注或代码注释,但更重要的是,动手写代码前先花3分钟想清楚数据规模、时间复杂度和核心数据结构。如果n是10^5,你还在写O(n^2)的循环,那基本等于浪费时间。

第三,忽视输入输出的边界条件。大厂笔试的隐藏用例往往包含极端情况,比如非常大的整数、空串、单个节点、重复元素极多的数组等。请务必在写完后自查边界,尤其是循环终止条件、数组越界、整数溢出这三种问题。

第四,动态规划的状态定义不清晰就开始写转移方程。状态定义错了,后面全盘皆输。动笔前先用一句话把dp[i]的含义写下来,再推导转移方程,最后再写代码,这个过程不能省。

第五,过度依赖本地IDE调试。在线笔试环境里没有“本地能跑、平台过不了”的说法,平台上不过就是不过。平时刷题就应该尽量在模拟在线判题的环境里练习,减少对IDE调试器的依赖。

5.2 考后如何查漏补缺

每场笔试结束后,我建议立刻做两件事。第一,把做错的每一道选择题都整理到一个错题本里,不只是记录正确答案,还要把错误原因写清楚,比如“排序稳定性记混了”“子网掩码算错了一位”。第二,把编程题重新在自己的IDE里完整写一遍,并且把这一题的解题套路归入自己的模板库,比如“子数组问题优先考虑前缀和”“依赖调度问题优先考虑拓扑排序+DP”。

我当时整理了一个自己的刷题优先级表,参考价值比较大,贴出来供你参考:

优先级知识点推荐练习方向
数组/哈希表/前缀和/双指针LeetCode双指针、前缀和标签下的中等题
链表/二叉树/递归二叉树遍历、最近公共祖先、链表反转
动态规划背包、最长子序列、编辑距离、区间DP
图论拓扑排序、Dijkstra、并查集
字符串处理滑动窗口、KMP、Trie
系统设计短链接、缓存、限流、消息队列

5.3 投递节奏与多厂对比

软件开发岗笔试最忌讳只盯着一家投递,然后孤注一掷。我当时是在秋招窗口期同时投了多家不同类型的企业,包括互联网公司、硬件厂商、云计算厂商和银行科技岗,这样有几个好处:第一,不同公司的笔试风格差异很大,有的重算法,有的重基础,有的重工程实践,多考几次能够覆盖更全面的知识点;第二,笔试本身也是高强度练习,每参加一场都能帮你发现一个知识盲区,这是光刷题得不到的反馈。

不过也要注意,不要盲目海投导致精力分散。我个人的策略是:把目标公司分成三档,保底档、匹配档和冲刺档,每一档投2到3家,集中精力准备共性的核心知识点,然后根据每家公司笔试侧重点做微调。比如投小米这类硬件厂商,我会重点复习操作系统和内存管理;投互联网公司,我会把算法题优先级提高;投银行科技岗,我会多看看数据库和SQL优化。

写在后面的一点经验

整场笔试复盘下来,我最深的感受是:大厂软件开发笔试其实是一场“基础 + 速度 + 取舍”的综合挑战,它在考察你是不是真的掌握了一个软件工程师日常必备的计算机功底,也在考察你在压力下能不能做出合理的策略选择。基础不牢,代码写得再花哨也没用;速度太慢,会的题也来不及做完;不擅取舍,就可能在小分题上耗死。

如果你正在准备软件开发方向的秋招笔试,我建议从现在开始,每周至少安排两到三次完整的模拟笔试,严格计时,不查资料,不做题外讨论,完全模拟真实考试环境。刷题之外,每天再抽30分钟过一个计算机基础知识点,比如今天看死锁,明天看TCP拥塞控制,后天看B+树索引,积少成多,比考前突击有效得多。最后再分享一个小技巧:每次笔试结束后,花10分钟在草稿纸上画一下整场考试的心智图——哪些题卡住、为什么卡住、下次遇到同类题应该怎么做——比单纯对答案有用得多。

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

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

立即咨询