
1. 先还原一下这套笔试卷的庐山真面目最近在整理旧资料时翻出了当年顺丰2017校招研发工程师的笔试题仔细重做了一遍感慨还挺多的。那会儿校招笔试还普遍是纸质试卷加答题卡时间90到120分钟题量大、知识点杂跟现在动辄线上OJ两道编程题的风格完全不是一个路子。先说这套试卷的整体结构记忆里的框架大致是四部分计算机基础知识含网络、操作系统、数据库、数据结构与算法、逻辑推理与行测类题型、以及一到两道开放性的场景设计题。单选、多选、判断、简答、编程题全都有覆盖面非常广。如果你以为研发岗笔试只考代码那就想简单了。顺丰毕竟是物流起家的科技公司它的研发笔试很有自己的行业痕迹。除了通用的计算机基础还会有快递业务场景题比如车辆配送路径如何规划分拣中心的数据怎么处理甚至直接问你如果双十一当天系统扛不住怎么办。这就决定了它的笔试套路和其它互联网公司不太一样不仅考你会不会写代码还考你能不能理解业务、能不能在极端场景下做技术决策。对于准备笔试的同学来说这套题最有参考价值的点在于——它的难度曲线非常典型。基础题占大头算法题不在数量多而在基本功是否扎实逻辑题则是区分度最高的部分。接下来我把能回忆起来的题目类型、考点和踩坑点逐个拆开说遇到典型题目也会给出详细解题思路希望能给正在准备校招或者社招笔试的朋友一些参考。2. 计算机网络与操作系统送分题里藏着杀招2.1 TCP建立连接的细节不是背完三次握手就够了顺丰这套卷子里网络部分的第一道题就是TCP三次握手的过程描述选项里混着几个看似正确其实错了的干扰项。这类题我在多个校招笔试里都见过它考的不是你知不知道三次握手而是你能不能区分SYN、ACK、seq、ack的准确含义。正确的流程是客户端先发送SYN报文seq为x服务端收到后回复SYNACKseq为yack为x1客户端再回复ACKseq为x1ack为y1连接建立。地方容易出错的地方是ack的数值和seq的数值傻傻分不清。握手过程中服务端回复的ack必须是客户端的seq1因为SYN报文本身要消耗一个序号。第一次C - S: SYN1, seqx 第二次S - C: SYN1, ACK1, seqy, ackx1 第三次C - S: ACK1, seqx1, acky1类似的衍生考点还有SYN Flood攻击的原理就是只发第一次握手不回应第三次让服务器维持半连接耗尽资源、为什么不能两次握手因为无法确认客户端的接收能力、TIME_WAIT为什么要等2MSL保证最后一个ACK能被对端收到同时让旧报文在网络中自然消失。这些我当时在准备时是整理成了一句话记忆搞不清顺序就画时序图画一次就记住了。2.2 进程与线程一不留神就掉进多选陷阱操作系统模块考了一道经典选择题以下关于进程和线程的说法正确的是。选项大概有进程是资源分配的基本单位线程是CPU调度的基本单位同一进程内的多个线程共享地址空间线程切换的开销一定小于进程切换进程之间可以通过共享内存通信答案是前两个和第四个第三个是错的因为一定这个绝对化表述在大多数情况下都有问题。跨进程切换需要切换页表、刷新TLB代价确实比同进程内线程切换高但跨进程和跨线程的比较不是这么简单的。同进程的线程切换虽然不涉及地址空间切换但涉及用户态和内核态的切换以及栈的切换开销并不算小。在真实场景里线程切换开销小于进程切换是普遍结论而不是绝对结论笔试选项里只要出现绝对一定必然这类词大概率就是坑。顺带一提这道题后面还跟了一道判断题死锁产生的四个必要条件是否包括资源互斥、请求保持、不可剥夺、循环等待。这是死锁的经典考法四个条件缺一不可。坑点在循环等待往往会被误写成等待队列中有多个进程这个说法不完整必须强调是循环的依赖关系才构成死锁。预防死锁的手段也常考资源一次性分配破坏请求保持、可剥夺资源破坏不可剥夺、资源有序分配破坏循环等待。银行家算法是避免死锁的经典方法考简答题时要能写出它需要维护的数据结构available、max、allocation、need。2.3 虚拟内存与页面置换LRU的代码考察页面置换算法是操作系统的高频题。顺丰这套卷子出了一道计算题系统采用LRU算法内存中有3个物理块访问序列是7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0问缺页次数是多少。这题本身不难但在笔试现场紧张的状态下容易算错。我建议按表格来推演访问页内存块1内存块2内存块3是否缺页77是070是1701是2012是0012否3123是最后算出来缺页次数是12次具体次数以实际推演结果为准这里不展开全部17步了。这类题只要注意一个原则就错不了发生缺页后淘汰的是最长时间未被使用的那一页而不是最晚进入的那一页。LRU考的不是概念而是对双向链表加哈希表这个实现结构的理解程度。3. 数据结构与算法题三道题看基本功3.1 单链表反转被考烂了也要背得滚瓜烂熟算法部分的第一题是手写单链表反转要求写出核心代码。这题在2017年的校招笔试里几乎人手一份但真正算法完整的人并不多。反转链表的迭代写法核心在于维护三个指针prev、curr、next每次先把curr的next存下来再改指向最后整体移动。struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev NULL, *curr head, *next NULL; while (curr ! NULL) { next curr-next; curr-next prev; prev curr; curr next; } return prev; }这里有两个容易翻车的点。第一最后返回的应该是prev而不是curr因为循环结束时curr已经指向NULL了。第二如果要求递归实现递归的返回值应该是反转后子链表的头节点而不是简单地把两个节点交换。我当时在简历里写了熟悉常用数据结构结果这道题花了好几分钟才写利索笔试结束后立刻回去把链表相关的题都刷了一遍这经验现在想想依然是血的教训。3.2 快递路径规划BFS求最少中转站顺丰的笔试题很接地气第二道算法题直接结合业务场景给定一个城市间航班或物流线路的邻接表求从城市A到城市B的最少中转次数。这个题其实就是求无权图的最短路径标准解法是BFS。核心思路是用队列维护待访问节点用一个visited数组标记已访问每扩展一层层次就说明中转次数加了一次。注意题目问的是中转次数还是经过的城市数如果是中转次数答案需要减一这个细节很多人踩过坑。from collections import deque def min_transfer(graph, start, target): queue deque([(start, 0)]) visited set([start]) while queue: city, transfers queue.popleft() if city target: return transfers - 1 # 中转次数 经过节点数 - 1 for neighbor in graph.get(city, []): if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, transfers 1)) return -1这道题的扩展方向也值得注意如果边有权重比如运输时间不同BFS就不适用了得换Dijkstra如果中转站数量有限制就变成了带层数限制的BFS本质上是在队列里多记一个维度。校招笔试能写对基础版BFS的人不少但能说清楚它和Dijkstra适用边界的才是加分项。3.3 排序与查找手撕快排的正确姿势编程题之外选择填空题里考了一张排序算法对比的表格时间复杂度O(nlogn)的排序算法有哪些稳定排序有哪些排序算法的空间复杂度分别是什么快速排序平均O(nlogn)但最坏O(n^2)归并排序空间复杂度O(n)且稳定堆排序空间O(1)但不稳定。这些知识点没太多技巧建议整理成一张表反复过排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性快速排序O(nlogn)O(n^2)O(logn)不稳定归并排序O(nlogn)O(nlogn)O(n)稳定堆排序O(nlogn)O(nlogn)O(1)不稳定插入排序O(n^2)O(n^2)O(1)稳定手撕快排的笔试还要注意partition函数的写法。我比较推荐Lomuto分区方案代码更短不容易出错虽然它在某些极端情况下的swap次数比Hoare方案多一些但笔试场景下代码简洁比微小的常数优化更重要。int partition(int[] arr, int low, int high) { int pivot arr[high]; int i low - 1; for (int j low; j high; j) { if (arr[j] pivot) { i; swap(arr, i, j); } } swap(arr, i 1, high); return i 1; }4. 数据库与SQL业务场景贯穿始终4.1 三张表的关联查询运单、订单、客户数据库部分的简答题直接给了物流业务背景有一张运单表tracking_number, order_id, status、一张订单表order_id, customer_id, create_time、一张客户表customer_id, name, city。要求查出上海市所有状态为已签收的运单所对应的客户姓名和运单号。这道题考的就是JOIN语法和多表关联能力。正确写法SELECT c.name, t.tracking_number FROM tracking t JOIN orders o ON t.order_id o.order_id JOIN customer c ON o.customer_id c.customer_id WHERE c.city 上海市 AND t.status 已签收;这类题在笔试里属于基础送分题但两个细节要注意一是JOIN的顺序小表驱动大表通常性能更好但这不是SQL的正确性问题而是优化问题笔试面试里提一嘴实际场景下需要考虑索引和驱动表选择会加分二是条件字段的索引情况city和status字段有没有建索引直接决定查询快慢。如果笔试允许额外写一句优化建议可以补上建议在tracking表的status字段和customer表的city字段上建立索引会显得你有实际项目经验。4.2 索引失效的几种典型场景另一道数据库相关的多选题是哪些情况下索引会失效。选项有对索引列使用函数如WHERE YEAR(create_time) 2025使用LIKE %abc前置通配符索引列进行隐式类型转换如varchar列与数字比较or条件中有一个非索引列四个选项全选。这题在笔试里出现的频率很高背后的原理是B树索引依赖有序性对索引列做函数操作会破坏原有顺序优化器无法利用索引前置通配符导致无法确定前缀顺序查找退化为全表扫描隐式类型转换会让优化器先对列做CAST功能上等同于对列使用函数。记住这些典型的反模式实际写SQL时才能下意识地避开。关于MySQL的索引写法顺丰这套卷子也考了一道基础题给一个表建立联合索引查询条件包含a、b两列问哪些查询能用上索引。联合索引遵循最左前缀原则只要查询条件里包含最左列a索引就可能被用到。a和b的查询顺序无关紧要数据库优化器会自动调整但只用b不用a是百分百用不上索引的。这块知识要是不扎实建议拿一张白纸动手画一画B树的查找路径比死记规则有用得多。4.3 事务特性与隔离级别ACID和脏读的辨析简答题里还考了事务的ACID特性。原子性Atomicity强调事务要么全做要么全不做一致性Consistency强调数据从一种合法状态到另一种合法状态隔离性Isolation是并发事务互不干扰持久性Durability是提交后永久生效。隔离级别从低到高分别是读未提交可能脏读、读已提交可能不可重复读、可重复读可能幻读、串行化最安全但性能差。MySQL默认是可重复读而Oracle默认是读已提交。这个差异在校招里考过无数次但很多人只背结论不理解原因。实际上MySQL的可重复读级别下幻读问题并没有完全杜绝只是InnoDB通过间隙锁解决了大部分场景。笔试中问到这个深度就不只是记忆力测试了。5. 逻辑推理题这才是真正的分水岭5.1 相遇与追及快递员和卡车的问题试卷的逻辑部分有一道经典的行程类应用题背景改成了顺丰的快递员和运输卡车。大意是快递员骑电动车从仓库出发匀速行驶45分钟后一辆卡车从同一仓库出发追赶卡车的速度是快递员的2倍问卡车追上快递员需要多少时间。解法很简单45分钟内快递员已经走了45v的距离v是车速卡车相对快递员的速度差是2v - v v追及时间 45v / v 45分钟。考场里这道题的正确率其实不高因为很多人习惯套路程 速度 × 时间这个公式却忽略了相对速度差才是追及问题的核心。这种题的价值不在数学难度而在能不能快速把现实问题抽象成数学模型。研发工程师日常做需求分析也是这样把业务语言翻译成技术语言再找到核心变量很多人掛在第一步翻译上。5.2 排列组合中的快递包裹编号逻辑题里还有一道排列组合有6个包裹要分给3个快递员每个快递员至少分到1个包裹问有多少种分配方式。假设包裹各不相同但快递员无区别还是包裹相同如果包裹各不相同、快递员也有区别标准解法是先不考虑至少1个的限制每个包裹有3种选择共3^6种再减去有人没分到的情形用容斥原理$$ 3^6 - C(3,1) \times 2^6 C(3,2) \times 1^6 729 - 192 3 540 $$如果包裹相同那就是隔板法C(5,2) 10。笔试里经常故意不写清楚包裹是否相同、人是否有区别这就是在考察建模时的假设条件。所以在做这类题的时候我强烈建议先在草稿纸上写明我假设什么再开始计算。宁可多写一个假设也不要让阅卷人猜你的思路。5.3 图形推理与语词理解非技术题的技术含量图形推理题在互联网校招笔试里一直饱受争议但不可否认它考察的短期模式识别能力跟debug时的找规律过程确实有相似之处。常见的规律包括旋转、翻转、对称、数量变化、封闭空间个数变化、笔画数变化等。顺丰的逻辑题难度不比公务员考试低遇到卡壳的题先放过回头再看往往能找到规律。语言理解类题目更像是在考准确表达需求的能力。比如给一段产品需求描述选出最能准确概括中心意思的选项或者给两个词选择逻辑关系类似的选项。这类题没有太多复习技巧多刷历年题找手感就行。6. 场景设计与业务思维物流场景下的系统设计6.1 双十一订单量暴增数据库扛不住怎么办最后一道大题是开放性的设计题假设双十一当天订单量暴涨10倍核心数据库负载过高你怎么设计解决方案这种题没有标准答案但阅卷人心里有一把尺子你能不能系统地给出分层优化方案。我当时的答题思路是从先保证可用、再提升性能的角度展开接入层Nginx做限流和负载均衡把请求均匀分发到多个应用节点应用层使用消息队列如Kafka削峰填谷将写请求先放入队列后台异步处理数据层分库分表按订单号或用户ID进行水平拆分读写分离主库负责写、从库负责读缓存层热点数据提前加载到Redis查询优先走缓存减少数据库压力兜底降级非核心功能如物流详情页、历史订单查询优先保障下单主流程这种题的答题技巧是总分结构先说总体思路再分点展开。千万别只写加服务器那是没有技术含量的答案。要体现出你知道瓶颈在哪里、每种手段分别解决什么问题。6.2 快递分拣路径的最优调度另一个场景题印象比较深在一个区域内有若干配送点每天有大量包裹需要从分拣中心运往各配送点要求设计一个算法帮助规划配送路线目标是总路程最短或总时间最短。这就是旅行商问题的实例版本。我知道精确解是NP-hard的所以在有限时间内只能启发式求解。我当时写了贪心最近邻算法加2-opt局部优化介绍了思路和时间复杂度虽然没有写代码但表达了自己知道这是近似解不是最优解的边界。后来面试环节面试官告诉我这道题想看到的作答方向是不要一上来就说我不会而是要能分析问题复杂度、提出可落地的近似方案、说明方案的适用边界。这一题给我最大的启发是研发岗位的笔试从来都不只是考察你会不会解这道题而是考察你面对一个现实问题时的思考过程是否成熟。很多时候思路比参考答案更重要。7. 复盘这套卷子给后来人的几点实用建议7.1 基本功的复习要成体系不能零散刷题回头看这套2017年的卷子它的知识点覆盖其实很规整网络、操作系统、数据结构、算法、数据库、逻辑、设计。每一块都给足了分数权重没有哪一类是不重要的。现在很多准备笔试的同学喜欢只刷LeetCode把大量时间花在算法题上结果计算机网络、数据库这类记忆型考点反而丢分。我的建议是搭建一个完整的知识树别让短板拖后腿。知识树可以按照这个思路来搭建网络层的TCP/IP、HTTP/HTTPS、DNS操作系统的进程、内存、文件系统数据库的索引、事务、SQL优化数据结构的数组、链表、栈、队列、树、图。每一类知识点准备三到五道典型题考前过一遍比刷200道算法题都管用。7.2 做笔试题的时间分配策略先易后难标记不确定校招笔试的时间通常比较紧尤其是前面选择题和判断题容易陷入纠结。我的经验是做题前先快速浏览一遍全卷粗略估计每部分的难度和工作量先做有把握的题标记不确定的题号留出最后10到15分钟集中回补。判断题要注意绝对化表述这个陷阱只要出现一定必定只要……就……这类词八成是错的。多选题宁少选别错选因为少选可能还有部分分错选直接零分。编程题如果一时间没有思路先把暴力解法写上拿到部分分再做优化别空着。7.3 开放题的答题套路不追求完美追求完整开放性的系统设计题最忌讳一个字都不写。哪怕思路不完整也要先把想到的要点列出来。写错了不会倒扣分空白才是真正的零分。答题的时候按总体思路 分点方案 边界说明来组织答案最后补一句以上方案在xx条件下可能失效需要根据实际数据量做进一步评估给人留下严谨的印象。举个例子我问过的很多候选人面对系统突然变慢怎么排查这类问题能答出先看CPU、内存、磁盘、网络的很多但能继续往下说CPU高要区分用户态还是内核态再看看是死循环还是GC频繁内存高要区分堆内存还是直接内存的就少很多。这就是完整度和深度的差距笔试答题也是一样的道理。7.4 心态与技能双准备最后说说心态。校招笔试是一次筛选筛选的不只是知识储备还有你在短时间内应对陌生问题时的抗压能力。这套2017年的顺丰卷子难度放在今天来看依然不低但它的每一道题都没有超纲到没学就不会的程度。只要在校期间认认真真学完计算机四大基础课再配合一定的刷题训练达标的可能性是非常大的。试想一下如果现在再给你出一套类似的试卷你能不能比当时那个自己答得更好进步的本质不是记住某道题的答案而是建立起一套可以复用的思考框架。这套框架才是比任何试卷都宝贵的财富。