2016年那阵子,我正好在准备校招,阿里巴巴研发工程师的笔试是很多人绕不开的一道关卡。网上流传的这套“阿里巴巴2016研发工程师笔试题(二)”,我前后刷过好几遍,也帮学弟学妹整理过完整解析。今天不打算把题目和答案逐条罗列出来,那没多大意义,我更想把它当成一个典型样本,拆一拆这套题背后的出题逻辑、考点权重、答题节奏和复习方法。如果你也在准备互联网大厂的技术笔试,或者想了解研发工程师到底考察什么,这篇文章应该能帮你少走不少弯路。
这套题表面上是几十道选择题加一两道编程题,但真正拉开差距的不是“背了多少概念”,而是你在有限时间内把问题拆解成代码、定位到具体知识点的能力。我会结合自己当年的应考经历和后来带新人时做的复盘,把数据结构和算法、操作系统、网络、数据库这些模块挨个过一遍,也会聊一聊考场上那些没人明说但特别实用的细节。
1. 这套笔试题到底在考什么:整体设计与出题逻辑
1.1 研发工程师笔试的核心筛选标准
很多人误以为笔试就是“考知识”,背得越多分越高。实际上,像阿里这类大厂的研发笔试,目标非常直接:用最短的时间,从几万份简历里筛出有代码感觉、有逻辑思维、基础扎实的候选人。笔试成绩不一定能让你拿到offer,但成绩太差,简历再漂亮也没有后续。
那“代码感觉”怎么量化?看数据结构题你能不能快速想到最优解,看编程题你能不能写出无死角的边界处理,看选择题你能不能判断出一个并发场景下的输出顺序。这套2016年的题有个特点:题目会刻意设置一些干扰项,比如把“进程与线程的区别”和“线程间共享资源”混在一起,把“HTTP状态码”和“TCP连接状态”放在同一个场景里。你要是只停留在死记硬背层面,很容易被绕进去。
所以复习时我反复提醒自己:不是把每个知识点背下来,而是能用这个知识点解释一个具体现象。笔试里的“为什么”比“是什么”重要得多。
1.2 当年阿里笔试题的题型结构与时间压力
从当年能搜到的真题复盘来看,这套题整体结构大致是:选择题占大头,其中既包含单选,也包含不定项选择,通常数量在20到25道左右;后面会跟一两道编程题,有时候是核心代码填空,有时候是完整手写函数。考试时间大概60到90分钟,说紧张是真紧张。
不定项选择是最坑的地方。选错一个选项整题不得分,漏选往往也只能拿到部分分。这意味着你不能凭感觉乱猜,必须在几个选项之间做确定性判断。编程题则更贴近工程环境:题目给一个场景,然后要求你实现一个函数或补充一段逻辑,但不会像IDE那样给你自动补全和编译提示。这也解释了为什么很多人LeetCode刷得很溜,一到笔试就翻车——习惯了看题目类型和测试用例,却忽略了现场环境里的调试成本。
我当年是把选择题的预期时间卡在40分钟以内,余下时间全部交给编程题和检查。这个安排不一定适合所有人,但原则是通用的:选择题再纠结也就一两分,编程题一个用例过不了可能就失去竞争力。先把能拿的分稳稳拿住,再去啃硬骨头。
2. 核心考点拆解:数据结构和算法才是绝对主线
2.1 数据结构热点:数组、链表、树、哈希表、堆
翻遍2016年这批题目以及同期大厂笔试题,数据结构部分的高频考点其实很集中,并不是每章都考。我自己整理过一张权重表,大致是这样的:
| 数据结构 | 常见考察方式 | 重要程度 |
|---|---|---|
| 数组/链表 | 原地操作、快慢指针、反转链表 | 很高 |
| 栈/队列 | 括号匹配、单调栈、队列实现栈 | 高 |
| 二叉树 | 遍历、重建、最近公共祖先、层序遍历 | 很高 |
| 哈希表 | O(1)查找、冲突处理、与数组的取舍 | 高 |
| 堆 | Top K、合并有序链表、定时器 | 中等 |
数组和链表为什么重要?因为它们是最底层的存储结构,几乎所有算法题都建立在它们之上。笔试里常看到的“给定一个链表,判断是否有环”,考察的就是快慢指针,而不是简单的链表遍历。二叉树更是重灾区,前序中序推后序、二叉树层序遍历、最近公共祖先这些题目年年变着法出。哈希表则更多出现在选择题里,比如问“哈希冲突的解决方法有哪些”,选项里会有开放定址法、拉链法、再哈希法之类的专业术语。
我的建议是:不要贪多,先把二叉树搞透,再把数组和链表相关的经典题刷熟练,最后用堆来解Top K问题。这些在笔试中的出现频率远高于“红黑树插入旋转”“B树删除合并”这类偏冷门知识点。
2.2 算法套路:动态规划、贪心、二分搜索、字符串处理
算法部分,2016年这套题也很典型:动态规划、贪心、二分查找、字符串处理是四张常考牌。
动态规划考的不是你背了多少经典题,而是能不能写清楚状态定义和转移方程。比如“最大连续子数组和”这种经典问题,很多人能记住代码,但问他“为什么状态转移是dp[i] = max(dp[i-1] + nums[i], nums[i])”,却解释不清楚。笔试不会直接这么问,但会换一个包装,比如“股票买卖的最佳时机”“最长上升子序列”。底层逻辑都是同一套:用当前状态承接历史最优,再与“另起炉灶”做比较。
贪心算法更隐蔽。常见的是区间调度、跳跃游戏这类问题。需要先通过排序让数据变得“局部有序”,然后再一步步做选择。最怕的不是不会做,而是用了动态规划去解一道贪心题,代码没错但复杂度太高,最后超时。
二分搜索看似简单,实际最难的是边界。我当年常犯的错是把while循环写成 left <= right,又在收缩边界时写错 left = mid + 1 或 right = mid - 1,导致死循环。笔试现场没有调试器,这类边界必须在平时就形成肌肉记忆。
字符串处理这块,KMP算法最近几年直接考背诵的概率下降了,但理解next数组的思想仍然有帮助。更多是考一些“判断回文串”“字符串匹配计数”等中等难度题。
2.3 手写代码的规范与边界条件
编程题不需要追求花哨,但必须完整、可运行。这里我用一道经典题“最大连续子数组和”来做示例,很多人第一反应是暴力双重循环,时间复杂度O(n²)。但如果数据范围是10万,笔试平台基本直接超时。正确的思路是用动态规划,时间复杂度降到O(n)。
public int maxSubArray(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int current = nums[0]; int best = nums[0]; for (int i = 1; i < nums.length; i++) { current = Math.max(nums[i], current + nums[i]); best = Math.max(best, current); } return best; }这段代码有几个关键细节值得注意。第一,为什么不把current初始化为0?因为数组可能全是负数,初始化成0会让最大值变成0,但正确答案应该是最大的那个负数。第二,为什么循环从i=1开始?因为我们已经把第0个元素作为初始状态,避免重复处理。第三,best变量必须独立保存全局最优,否则最后返回的可能是局部状态。
写这类题时,我通常会在代码上方用一两行注释说明思路,比如“dp思想:要么从当前元素重新开始,要么带上前面的累加和”。笔试阅卷有时候是人工,有时候是自动用例,但清晰的注释至少能在人工环节加一点印象分。更重要的是,养成先考虑空数组、单元素数组、全负数数组这些边界条件的习惯。
3. 非算法模块:基础理论才是选择题的得分点
3.1 操作系统与并发
操作系统在选择题里考得非常稳定,几乎每年都有好几道。进程和线程的区别是必考项,但不会直接问“什么是进程”,而是给你一个场景,比如“多线程程序里哪些数据是线程共享的,哪些是私有的”。答案是:堆空间和全局变量是共享的,栈空间和寄存器是私有的。这个考点看似简单,却总有人在“栈是不是共享”上栽跟头。
死锁也是高频考点。死锁的四个必要条件(互斥、持有并等待、不可剥夺、循环等待)需要背下来,更重要的是会判断一个实际场景是否可能死锁。我记得当年有一道题模拟了两个线程各自持有锁,然后互相请求对方的锁,问最终结果。如果你理解“循环等待”的本质,一眼就能看出来这是典型的死锁。面试官还会顺着笔试题目追问“如何避免死锁”,这种题目在笔试里可能只占一分,但背后延伸出来的问题能影响整场面试。
内存管理也值得花时间。分页和分段、虚拟内存、页面置换算法中的LRU,这些要能说清楚概念,还要能做简单计算。比如“页面大小为4KB,虚拟地址0x12345678对应的页号和偏移量是多少”,这类题考察的就是对地址转换过程的熟悉程度,不是靠猜能蒙对的。
3.2 计算机网络
网络部分的重点非常集中:TCP/UDP、HTTP、DNS。每次笔试都绕不开TCP三次握手和四次挥手,但考察方式经常在“TIME_WAIT是在哪个状态之后进入的”“为什么需要TIME_WAIT”这样的细节上。我见过不少人能画出三次握手流程图,却说不清TIME_WAIT的作用有两个:保证最后一个ACK能到达对端,以及让旧连接中的延迟报文在网络中消失。
HTTP状态码也是选择题常客。2016年前后HTTP/2已经开始普及,但大部分题目还在围绕HTTP/1.1的细节,比如304 Not Modified表示什么、503 Service Unavailable表示什么。如果你对常见状态码只有模糊印象,很容易在“301和302的区别”上丢分。301是永久重定向,302是临时重定向,语义不同,导致浏览器缓存策略也不同。
DNS解析流程同样要会。从浏览器缓存、系统缓存、本地DNS服务器到根域名服务器、顶级域名服务器、权威域名服务器,整个过程要能画出步骤。值得注意的是,很多选择题会把“递归查询”和“迭代查询”混在一起,你要分清是“主机到本地DNS服务器”通常是递归,还是“本地DNS服务器到其他DNS服务器”通常是迭代。
3.3 数据库与SQL
数据库在研发工程师笔试中占比不算最高,却极易拉开分数。核心考点是索引结构、事务特性和SQL写法。B+树作为主流索引结构,几乎每年都会出现,问的无非是“为什么用B+树而不是B树或红黑树”。思路是:B+树非叶子节点不保存数据,相同页能存储更多索引项,树高更低;叶子节点形成链表,适合范围查询。这也是InnoDB默认索引选择B+树的原因。
事务这一块重点掌握ACID四个特性,以及不同隔离级别能解决什么问题。读未提交会有脏读,读已提交能避免脏读但会有不可重复读,可重复读能避免不可重复读但可能有幻读,可串行化最严格但性能最差。MySQL默认的InnoDB隔离级别是可重复读,但通过间隙锁能很大程度上避免幻读。这类题目在选择题里经常以“某隔离级别下会不会出现某种现象”的形式出现。
SQL题目大多围绕“分组取每个类别的最大/最新记录”来出。2016年MySQL窗口函数还不像现在这么普及,所以很多人用自连接或临时表来解。如果你现在准备笔试,可以直接使用row_number()、rank()这类窗口函数,简洁且不容易出错。但也要理解老式写法的逻辑,因为有些平台环境未必支持窗口函数。
3.4 语言基础与工程知识
语言基础属于“看着简单,丢分可惜”的模块。C++和Java是主流选项,你不需要两门都精通,但至少要掌握一门。题目通常给一段小代码,让你判断输出。比如Java中final关键字修饰变量时的初始化时机、static代码块与构造函数的执行顺序、C++中虚函数和纯虚函数的区别。
这些题目考察的其实是“语言底层发生了什么”,而不是“语法怎么拼”。比如Java对象在堆上分配、垃圾回收怎么判断对象可回收、C++析构函数为什么要写成虚函数。我在复习时是用“讲给室友听”的方式:如果我能把某个机制用最简单的话说明白,说明真懂了。
工程知识里偶尔还会出现设计模式、Linux常用命令、Git操作。Linux的awk、grep、find这些命令有时候会在选择题里出一两道,不需要背太多,但高频的几个最好知道。Git的merge和rebase区别也是热门题。如果时间充裕,花半天把这类工程工具题过一遍,性价比很高。
4. 实战模拟与答题策略:做题顺序与时间分配
4.1 拿到卷子先做“信息侦察”
很多人拿到笔试链接就急着开始答题,我建议先花一两分钟浏览整张卷子,了解题型和题目数量。特别是看有没有编程题,有的话有几道,当前平台是ACM模式(自己处理输入输出)还是LeetCode模式(只需要实现函数)。这个信息直接决定时间分配。
我的习惯是:先把所有选择题快速过一遍,标记出“一眼就会”的题,这类题优先做完拿分;再解决“需要算一算”的题;最后才啃“完全没有思路”的题。这样做的好处是,在时间压力下能保证基本盘。如果你一上来就和某道难题死磕,很容易导致后面简单题没时间做,情绪还会越来越急躁。
4.2 选择题的排除法与“确定性优先”
面对不确定的选择题,我先讲一个原则:在不定项选择里,宁可少选,不要错选。很多平台的计分规则是漏选得部分分,错选为零分。如果你的目标是“拿到尽量多的分数”,不确定的选项就不要勾。当然,你要先看清题目说明,因为不同平台规则可能不一样。
排除法最好用的场景是概念题。四个选项里有两个明显概念不对,剩下两个即使拿不准,猜对的概率也高很多。还有一种“量纲法”,比如算地址偏移量或系统吞吐量时,可以先看单位是否合理,再代入公式。遇到需要计算的题目,不要心算,打草稿。网上笔试也会有电子草稿纸,但在线系统往往不支持复杂的数学公式,用纸笔更靠谱。
4.3 编程题的输入输出与边界测试
编程题是笔试中权重最高、最容易翻车的部分。我强烈建议你提前确认目标公司的笔试平台环境:核心代码模式还是ACM模式。2016年阿里用的平台更接近“补齐函数核心逻辑”,但也会要求你处理输入数据。无论哪种模式,代码都必须能通过多个隐藏用例。
写编程题时,我习惯按这个顺序思考:先明确输入范围,根据复杂度选择算法;再处理边界条件;再写主体逻辑;最后自己构造几个测试用例。比如求最大子数组和那道题,至少要测全负数数组、单元素数组、全正数数组、空数组。边界测试不需要写进代码,但在心里过一遍,能提前发现很多问题。
还有一点容易被忽略:尽量不要在代码里用平台不保证支持的语法特性。比如Java 8和Java 11在某些API上有差异,如果你为了写起来爽用了var,但平台编译器版本较老,直接编译失败。稳妥一点,用最基础、通用性最强的语法。
5. 常见问题与避坑指南
5.1 复习阶段最容易踩的坑
第一个坑是只看题解不手写。当年我身边有几类人,LeetCode刷了两三百题,但笔试编程题照样崩。原因很简单:看题解时你是“被动理解”,代码逻辑是顺畅的;一旦合上答案自己写,就会出现各种语法错误、边界遗漏。所以我每次刷题后都会强制自己在空白文档里重新写一遍,不打开之前提交记录。
第二个坑是刷题没有分类。很多人今天做链表明天做图论,知识点散成一盘沙。建议按专题刷,比如连续一周只刷动态规划,把常见模型都见过一遍。这样做的好处是能够总结出共性:背包类、区间类、序列类,状态定义和转移思路其实有迹可循。当年的笔试题虽然题目不同,但解法套路高度相似。
第三个坑是低估了选择题里基础理论的重要性。算法题能拉分,选择题同样能拉分。如果你的操作系统、网络、数据库基础不牢,只看算法题,很可能笔试总分不够。我见过不少算法好但基础薄弱的人,最后栽在选择题上。记住:笔试是总分游戏,不是单科竞赛。
5.2 笔试现场的典型失误
- 在一道选择题上恋战超过3分钟,导致后面编程题时间不够。实际上,选择题分值有限,蒙一个赶紧走人比死磕划算。
- 没有看清单选还是多选。系统一般会标明,但紧张时容易忽略。多选当单选做完,分数直接腰斩。
- 编程题只考虑了正常输入,忽略了空输入、最大值溢出、重复元素等边界。
- 手写代码时忘记加分号或写错括号。在线笔试没有自动补全,平时在IDE里写惯了的人最容易翻车。
- 没有点“保存/提交”或者提交太晚导致答案丢失。这个听起来很蠢,但每年都有。
5.3 从笔试到面试的衔接点
笔试结束不等于万事大吉。大厂面试官经常拿着你的笔试代码来追问,比如“你这道题还有没有更优解法”“这个边界条件如果输入特别大怎么办”“为什么选择这种数据结构而不是另一种”。所以笔试时写的每一步都要能讲出理由。
我当年面研发岗的时候,面试官就追着笔试题里一段代码问了很久,从时间复杂度问到是否了解空间局部性,再问到如果数据存在磁盘上怎么处理。那个问题本质上就是用合理的数据结构解决大规模数据下的Top K问题。如果你笔试时只是背了答案,这些追问很容易让你露馅。
所以从笔试到面试,我会建议你做一个额外动作:每做完一套题,把涉及的知识点写成“一句话笔记”,比如“快排的partition函数可以用来求第K大,平均O(n),但最坏会退化”。这样等笔试通过后,你能快速回忆起当时的思路,而不是等到面试前再重新翻题。
6. 如何用这套题做更高价值的复盘
6.1 建立自己的错题本与考点矩阵
笔试结束后,最忌讳的是看一遍答案然后不管了。一道题做错,往往不只是一个知识点不会,而是背后某些底层思维没建立起来。我会把错题整理到表格里,按考点分类,记录错误原因、正确解法、相似题。最终形成一个自己的考点矩阵。
| 考点 | 掌握程度 | 易错原因 | 复盘动作 |
|---|---|---|---|
| 数组/链表操作 | 熟练 | 边界条件容易漏 | 写链表题先画图 |
| 二叉树遍历 | 熟练 | 递归转迭代不熟 | 练习栈模拟递归 |
| 动态规划 | 一般 | 状态定义不清晰 | 总结常见状态套路 |
| 死锁 | 熟练 | 判断场景不够灵敏 | 刷场景题 |
| TCP状态 | 一般 | TIME_WAIT理解不深 | 画状态转移图 |
| SQL窗口函数 | 不熟 | 平时用得少 | 手写分组排序题 |
这个矩阵能帮你快速定位弱项,避免盲目刷题。重点不是“今天错了哪一道”,而是“这一周有没有把一个薄弱点彻底补上”。
6.2 时间轴建议:考前一个月怎么安排
如果你已经有了明确的目标公司笔试,我建议按四周来准备。第一周,把这套题及同类型题目完整做一遍,不卡时间,但把不会的题标记出来;第二周,按专题突破,重点补自己最不熟练的模块;第三周,模拟真实笔试,定好闹钟,严格按照考试时间做整套题目;第四周,回归错题本,把高频错点和容易被问到的原理复习一遍。
模拟时要尽量还原真实环境:关闭聊天软件,不开IDE自动补全,不翻资料。因为远程笔试虽然没人盯着你,但自我约束越严格,考场上越不会慌。
6.3 保持题目新鲜感的技巧
刷题很容易产生“厌倦感”,尤其是同一类题目做多了以后。一个小技巧是:每做完一道题,不直接看答案,而是写一段“我为什么这样做”的笔记。写完以后你会发现,很多当时觉得“怎么想出来的”的题,其实背后都有固定思维链。
另一个方法是“反向出题”。比如你学会了最大连续子数组和,可以反过来想,如果题目改成“最大连续子数组积”该怎么处理?这样一道经典题能延伸出三四种变体,比你盲目刷新题效率高得多。这套2016年的笔试题网上能找到的讨论和解析不少,但很多人只是把它当作“题库”,刷完就忘了。实际上,把这些题拆开揉碎,归纳成自己的知识网格,价值会大得多。后来我每次带新人准备笔试,都会让他们从这套题入手,先把基础模块吃透,再去挑战更高难度的题目。
回过头看,2016年的这套题在技术上并没有多超纲,但它在有限时间内准确测试了一个人的基础知识面、代码功底和临场取舍能力。我自己最大的收获不是“作对了几道题”,而是通过复盘,逼着我把操作系统、网络、数据库这些平时不太常用的东西重新整理了一遍。最后再分享一个小技巧:每次模拟完笔试,我会把“时间分配是否合理”这件事记录下来,哪类题超时了、哪类题其实可以秒答,都要写清楚。别小看这一步,它比多刷十道题更能提升实战表现。