
2013年秋天我参加了阿里巴巴研发工程师的校招笔试。那个年代互联网大厂的笔试还没有全面搬到线上多数还是纸质试卷两个小时一叠A3纸正反两面印满题目。考完出来候考区一片安静不是放空是大家都在回忆自己哪道题没写完、哪道题完全没思路。平心而论这份卷子难但不是拿偏题怪题来刁难你的难。它的难在于所有题目都长在计算机基本功的“预期之内”可一旦你学得不透现场立刻现原形。它考察的范围往大了说是整个计算机专业核心知识体系往细了说就是算法、数据结构、操作系统、网络、数据库、逻辑推理这几块。对今天还想冲一线互联网公司研发岗的同学来说这套卷依然是非常好的自测题——如果你能稳定拿下七成以上说明基础已经相当扎实。1. 一份2013年的笔试卷画出了怎样的知识版图拿到卷子先别急着做题先花两分钟把整张卷子扫一遍。我当时的直观感受是算法和数据结构占了绝对大头操作系统、网络、数据库紧随其后最后还会有几道逻辑推理和开放性设计题。下面这个分布是我根据记忆整理的不同岗位方向会有差异但大格局基本是这样。考察板块大致占比典型考察点算法与数据结构35% - 40%链表、树、排序、动态规划、字符串处理操作系统10% - 15%进程线程、死锁、内存管理、并发计算机网络10% - 15%TCP/IP、HTTP、Socket通信数据库5% - 10%SQL编写、索引原理、事务特性编程语言与代码阅读10%指针、内存、类与对象、代码输出逻辑推理与开放题10%智力题、系统设计、场景分析1.1 为什么大厂笔试总爱“大而全”很多人不理解我投的是Java研发岗为什么还要考操作系统和网络为什么不直接考框架、考项目这里要明白笔试的定位它是海选不是决赛。一天要筛几千份简历笔试就是要用尽可能客观的方式快速判断一个人的“底层素质”。框架可以进公司再学但操作系统、网络、数据结构这些是没法速成的。一个人能不能在两个月内上手公司的业务代码取决于他有没有扎实的计算机基础。笔试考的就是这个。2013年的笔试还有个特点很多题表面考基础实际在往“工程直觉”上引。比如数据库的索引题会追问“如果一张表有上亿条数据怎么让查询变快”网络题会问“服务端出现大量TIME_WAIT是什么原因怎么处理”。这些题目背后是当时互联网业务普遍遇到的高并发、大数据量问题。1.2 2013年的技术语境决定了出题风向那一年移动互联网正处爆发期云计算刚刚起步阿里内部在推进“去IOE”分布式、缓存、消息队列这些词开始频繁出现在技术讨论里。所以笔试卷里出现并发、缓存、数据库索引相关的内容完全不意外。我记得当时有题目涉及“多个线程同时写一个变量怎么保证正确性”以及“用SQL统计一张订单表里每个用户的消费总额”。这些题今天看依然经典因为它们对应的就是真实业务场景。备考时如果只盯着算法题刷忽略这些基础理论很容易在同一张卷子上摔跟头。2. 算法题占了半壁江山那些年被反复翻牌的经典题笔试卷的算法题不会出那种脑筋急转弯式的偏题基本都是经典题的变体。链表、二叉树、字符串、动态规划翻来覆去就这些。但经典题不等于简单题恰恰是因为经典面试官才能在同一道题上看出不同人的水平差距。2.1 链表与树看起来送分实则处处是坑链表题里最高频的是这几类判断链表是否有环、找环的入口、反转链表、合并两个有序链表、找倒数第K个节点。二叉树则是层次遍历、求深度、判断平衡、找最近公共祖先。拿“判断链表是否有环”来说标准解法是快慢指针快指针每次走两步慢指针每次走一步如果链表有环两者必然相遇。很多人知道这个结论但没想过为什么。假如快指针每次走三步还能保证相遇吗不一定。因为快慢指针的相对速度是1步在环上快指针每个单位时间靠近慢指针一步总能追上但如果相对速度不是1就可能出现快指针“跳过”慢指针的情况。所以考场上一旦题目变形比如“判断两个链表是否相交”死记硬背的人就懵了。再如二叉树最近公共祖先递归写法非常简洁def lowest_common_ancestor(root, p, q): if not root or root p or root q: return root left lowest_common_ancestor(root.left, p, q) right lowest_common_ancestor(root.right, p, q) if left and right: return root return left or right这题考察的其实是递归思维把问题拆成“在左子树找”和“在右子树找”。写起来只有几行但能把递归理解不到位的人卡死。2.2 动态规划一道题就能拉开差距2013年那批笔试卷动态规划几乎是必考的,最长递增子序列、0-1背包、编辑距离都是常客。这类题难不在写代码而在“看出这是动态规划问题”并准确写出状态转移方程。拿最长递增子序列来说状态定义是dp[i]表示以第i个元素结尾的最长递增子序列长度转移方程是dp[i] max(dp[j] 1) 对所有 j i 且 nums[j] nums[i]最后答案不是dp[n-1]而是所有dp[i]的最大值。这个细节很多人栽过。更进阶的做法是用贪心加二分把复杂度从O(n^2)降到O(n log n)维护一个递增的“最小末尾”数组。如果笔试时间充裕写出O(n^2)已经能拿大部分分但你要在复杂度分析里明确说清楚。为什么大厂爱考动态规划因为它考察的是抽象建模能力——把一个看似复杂的问题拆解成若干重叠子问题并找到递推关系。这种能力在真实工程中同样重要比如优化一个多阶段决策流程本质就是在做动态规划。2.3 手写快排比想象中更能暴露问题“请手写快速排序”几乎是所有笔试的保留节目但能一次写对的人并不多。def quicksort(arr, left, right): if left right: return pivot arr[left] i, j left, right while i j: while i j and arr[j] pivot: j - 1 arr[i] arr[j] while i j and arr[i] pivot: i 1 arr[j] arr[i] arr[i] pivot quicksort(arr, left, i - 1) quicksort(arr, i 1, right)这版写法用挖坑法避免了频繁swap逻辑清晰。但考场上容易错的地方是内层循环忘记加i j条件导致越界或者比较符号用错导致死循环。更讲究一点还会问快排是稳定的吗不是。最坏时间复杂度是多少O(n^2)发生在每次分区都极度不平衡时。怎么缓解随机选pivot或三数取中。一个只背模板的人和一个真正理解partition过程的人写的代码一眼就能分辨。前者在边界条件上犹豫后者能顺手讲清楚为什么pivot的选择会影响性能。3. 纸上写代码数据结构基本功到底考到什么程度除了算法题笔试卷里经常会出现“手写实现XXX数据结构”或者“阅读程序写出输出结果”这类题。这比算法题更直接地考察代码功底因为没有IDE提示没有编译器的容错写错了就是写错了。3.1 从“会用”到“能写”栈、队列与哈希表ArrayList、HashMap谁都会用但让你不用库函数用数组实现一个栈或者用两个栈模拟队列很多人会卡壳。用数组实现栈核心就是维护一个top指针class MyStack { private int[] data; private int top -1; public MyStack(int capacity) { data new int[capacity]; } public void push(int x) { if (top data.length - 1) throw new RuntimeException(stack overflow); data[top] x; } public int pop() { if (top 0) throw new RuntimeException(stack underflow); return data[top--]; } }这题本身不复杂考的是你对“栈”本质的理解它是受限的线性表只在表尾进行插入和删除。细节上还要考虑栈溢出和栈空两种情况——职场里我们叫“防御式编程”。笔试时能主动把异常分支写出来的人通常代码习惯比较好。哈希表更是结构题里的重头戏。常见的考察方式有实现一个简易HashMap说明哈希冲突怎么处理解释为什么HashMap的扩容要重哈希。链地址法和开放定址法都要能说清楚前者是每个桶挂一个链表冲突了挂后面后者冲突了就找下一个空位。Java的HashMap用了链地址法且在链表过长时会转成红黑树这是后面版本才有的优化但2013年的考卷基本还停留在“链表法”层面。3.2 指针、边界与内存纸上代码最常见的三大杀手我记得有一道题是反转单向链表题目简单但给了三个前置要求不使用额外空间、注意空链表、注意只有一个节点的链表。这道题就是典型的“陷阱题”。原地反转链表的迭代写法如下struct ListNode* reverseList(struct ListNode* head) { struct ListNode* prev NULL; struct ListNode* curr head; while (curr ! NULL) { struct ListNode* nextTemp curr-next; curr-next prev; prev curr; curr nextTemp; } return prev; }看似简单但很多人会漏掉保存nextTemp这一步。直接写curr-next prev链表在第一步就断了。这种错误在笔试里非常典型因为它只出现在“手写代码”场景下IDE里写的话编译器马上会提醒你。所以纸上写代码做的其实是“代码走查”训练写完必须自己用几个例子在脑子里模拟执行一遍。内存相关的题也是常客。比如“以下代码有什么问题”给出一段申请内存后没有释放的C代码问你内存泄漏发生在哪里。现在Java、Go用多了很多人对内存不敏感但笔试考的就是你有没有这个意识。3.3 复杂度分析答案对一半复杂度算错全扣很多笔试题目会明确要求“写出时间复杂度和空间复杂度”。算法题如果分析错了复杂度即使代码逻辑对也要扣分。这反映的是你对自己写的代码有没有全局认知。为什么复杂度分析这么重要因为面试官想知道你给的解法能不能在真实数据量下工作。半小时跑不完的算法和跑不完的服务器一样都是事故。主定理可以帮你分析递归算法复杂度但笔试现场来不及推导常用的结论最好直接记住归并排序O(n log n)、快排平均O(n log n)、二叉树遍历O(n)、二分查找O(log n)、双重循环O(n^2)。还有一类题是让你优化。比如“给一个整数数组找两个数使得和为target”暴力是O(n^2)用哈希表可以降到O(n)。这种题考的不只是思路还有你对空间换时间这个基本策略的理解。笔试里能主动写出优化解的人通常对复杂度的敏感度很高这也是大厂最看重的代码素养之一。4. 基础理论题操作系统、网络、数据库没有一道是背出来的说实话算法题可以靠刷题堆出来但操作系统、网络、数据库这些基础理论题很难靠短期突击蒙混过关。因为考官喜欢追问“为什么”只背结论往往撑不过第二问。4.1 操作系统并发与内存管理是两大主线进程和线程的区别几乎年年考。进程是资源分配的基本单位线程是CPU调度的基本单位同一进程的线程共享地址空间进程之间地址空间相互隔离。这个考点背后是真实工程里的线程安全、锁竞争问题所以它从来不只是一道概念题。死锁的四个必要条件——互斥、持有并等待、不可剥夺、循环等待——也是高频考点。题目通常有两种出法一是列出四个条件让你判断是否死锁二是给一个场景问你如何避免死锁。用生活场景类比就好懂四个人围一桌每个人左手拿一根筷子右手去拿旁边人的筷子谁都不肯放手这就是死锁。解决办法要么让大家同时放下筷子破坏持有并等待要么规定一个拿筷子的顺序破坏循环等待。内存管理里虚拟内存、页面置换算法是常客。LRU最近最久未使用几乎是必考因为它在缓存系统里也用得到。实现LRU需要哈希表双向链表这个数据结构题同时考察了操作系统和代码能力是典型的复合考点。4.2 网络三次握手四次挥手背后的“为什么”TCP的三次握手大多数人能背出状态流转SYN_SENT、SYN_RCVD、ESTABLISHED。但如果问“为什么是三次而不是两次”很多人就愣住了。原因是三次握手要确认双方的收发能力都正常。第一次握手客户端确认自己发送能力OK、服务端接收能力OK第二次握手服务端确认自己发送能力OK、客户端接收能力OK第三次握手客户端再确认一遍双方OK然后建立连接。如果只有两次会出现一种情况客户端发送了一个SYN但因为网络延迟服务端收到后又返回ACK此时服务端以为连接建立会分配资源等待数据但客户端可能已经超时重传或者放弃了于是这个连接就变成了服务端单方面的资源占用。第三次握手的目的就是防止这种“已失效的连接请求”让服务器白白浪费资源。四次挥手的核心是TIME_WAIT。主动关闭连接的一方在发送最后一个ACK之后要进入TIME_WAIT等2MSL。一是为了保证最后一个ACK能到达对方如果丢了还能重发二是要让旧连接上的所有报文在网络中消逝避免影响新连接。笔试里如果只写了“握手挥手各几次”而没解释原因拿到的往往是基础分。HTTP也是网络板块的重头。2013年的卷子会考状态码200、301、302、404、500这些要能说清含义还要能结合场景判断“用户访问网页时收到302说明什么”。扩展问就是Cookie和Session的区别以及HTTP的无状态性怎么解决。这种题现在看虽然基础但确实是服务端开发每天都要接触的东西。4.3 数据库索引、事务与一条SQL的优化数据库考题里最经典的是“为什么InnoDB用B树做索引而不是红黑树”。答案的核心是磁盘IOB树矮胖扇出高三层就能存千万级数据查询时磁盘IO次数少红黑树高瘦节点不存多条数据查找一个数据要访问更多节点在内存里无所谓在磁盘上就是致命的。事务的ACID特性也常考尤其喜欢结合场景问隔离级别。比如“两个事务同时更新同一行会发生什么”答案取决于隔离级别读已提交会出现不可重复读可重复读会解决这个问题但可能出现幻读。MySQL默认的可重复读通过间隙锁在一定程度上解决了幻读。能把这个链条讲清楚说明你是真的理解而不是背概念。SQL题基本上是必考的。常见的考法有查每个部门工资最高的员工、查连续登录N天的用户、统计订单表按月销售额。比如“每个部门工资最高的人”SELECT d.name, e.name, e.salary FROM employee e JOIN department d ON e.dept_id d.id JOIN ( SELECT dept_id, MAX(salary) AS max_salary FROM employee GROUP BY dept_id ) t ON e.dept_id t.dept_id AND e.salary t.max_salary;先按部门分组求最大工资再关联回员工表取详情这个过程考察的是对SQL执行逻辑的理解。很多人一上来就写GROUP BY但忘了“分组之后只能查聚合列”取不到员工姓名。笔试现场能写出上面这种关联子查询基本就能拿满分。5. 逻辑推理与开放设计题真正拉开差距的部分基础题大家都会准备但逻辑推理题和开放设计题是真正拉开差距的地方。这类题没有题库可以刷靠的是平时的思维习惯。5.1 经典逻辑题25匹马、找假币、烧绳子25匹马5个赛道最少比赛几次能找出最快的3匹这是当年流传很广的一道题答案是不超过7次。过程是先分5组每组5匹各跑一次决出每组第一名共5次。第六次让5个组第一名跑一次决出总第一名同时得到各组排名。假设A1 B1 C1 D1 E1那么候选最快三匹只有A1、A2、A3、B1、B2、C1共6匹。但第六次之后A1已经确定是总第一剩下只需要从A2、A3、B1、B2、C1中选出前二。第七次让这5匹跑一次取前两名即可。这道题考察的核心是“剪枝”——如何通过有限次实验尽快缩小候选集。很多人一开始会说“逐组比较”或“所有马跑一次取前三”这两种思路要么次数多要么忽略了每匹马在不同组里的相对表现。笔试里的逻辑题往往考的就是你能不能跳出“逐个比较”的惯性思维。类似的还有“9个球其中一个偏重用天平最少称几次找出来”。答案是2次先分成3组每组3个第一次称两组如果平衡重的在第三组如果不平衡重的在偏重的那组。第二次从重的组里取两个称一下同样能锁定。这类题的核心是“信息量”思维——每次称重最多有三种结果所以两次最多能区分3^29种情况刚好覆盖9个球。5.2 开放式设计题没有标准答案只有思路等级开放题一般是“设计一个短网址系统”或者“设计一个分布式缓存”。这类题没有标准答案考官看的是你回答问题的层次。低分回答直接用一张表存长网址和短网址的映射短网址用随机字符串查的时候查一下表。中等回答考虑短网址的生成方式比如用发号器自增ID转62进制查询时用缓存减少数据库压力服务器做负载均衡。高分回答从全局考虑发号器怎么做高可用多节点发号怎么避免重复缓存如果挂了怎么办短网址的跳转用301还是302301会永久缓存导致无法统计点击数所以一般用302数据库分库分表的方案以及如何应对恶意请求。看出差距了吗同一个问题低分回答是“能用”高分回答是“能扛住”。笔试卷面向的是研发工程师不是架构师但能在开放题里展现出架构意识的候选人一定会被重点关注。2013年的卷子能考这些说明出题人已经在筛选“有全局视野”的人了。6. 今天重新做这套卷我会怎么准备时隔多年回头看当时成绩不错的人未必是刷题最多的但一定是基础最扎实、遇到问题习惯性追问“为什么”的。如果你现在也要面对类似的笔试我给你几条不掺水的建议。6.1 刷题策略与其刷量不如刷“透”很多人准备笔试就是疯狂刷LeetCode一天十道刷完就忘。我的建议是反过来一个知识点一个知识点地过。链表题做十道不如把反转链表、判断环、合并有序链表的原理彻底搞懂再总结出这类题的通用解法。错题比新题重要复习旧题比做新题重要。每天给自己出三道题限时完成模拟笔试节奏。写完不是对完答案就结束一定要复盘为什么刚才没想到这个思路卡在哪个环节下次遇到类似的题怎么切入这三个问题比“我AC了”更有价值。6.2 手写代码训练把编辑器关掉笔试现场没有编译器没有代码提示写错了就是错了。所以必须练“无辅助写代码”的能力。找一张白纸定时二十分钟手写一道中等难度的算法题写完再用眼睛走查一遍边界条件。一开始会很别扭但练上两周你会发现自己写出来的代码“一次通过率”明显提高。还有个小技巧手写代码时把变量名写完整逻辑写清晰不要写出“a、b、c”这类让人看不懂的命名。笔试虽然不强制你写得像开源项目一样规范但代码整洁度好的卷子确实更容易得到阅卷者的好感。6.3 理论基础课怎么补网络、操作系统、数据库这么大的知识面突击一个月够吗够但要用对方法。不要从头到尾啃书而是用“问题驱动”的方式把历年高频考点整理成一个个问题然后针对每个问题回答“是什么、为什么、怎么用”。比如网络就整理成一系列问题三次握手为什么是三次TIME_WAIT有什么用TCP粘包怎么处理HTTPS的握手过程是怎样的每个问题自己先回答一遍再看标准答案补充。这样过一遍既快又牢比“看了三遍书合上就忘”强太多。数据库的SQL题一定要亲手在电脑上跑一遍。光看不练连GROUP BY和HAVING的执行顺序都可能搞混。准备一张测试表自己造数据把“每个部门工资最高”“连续登录天数”这类经典题写一遍理解了执行顺序和索引原理上考场才不慌。说到底笔试考的是基本功基本功没有捷径。2013年的这份卷子本质上是把所有候选者拉回同一条起跑线不看谁项目多、不看谁实习牛就看你计算机底子厚不厚。我后来也参与过几轮校招命题发现出题人的心态一直没有变过他们不是想难倒你而是想通过一张卷子找到那些真正理解技术底层的人。你能做的就是让自己成为这样的人。