
每年到了春招秋招就有不少朋友翻出各种大厂真题来刷。我后台也经常收到关于滴滴研发工程师笔试的私信尤其是这套“滴滴出行2016研发工程师笔试题三”被问到的频率非常高。仔细想想也不奇怪2016年正是移动出行行业竞争最激烈的时候滴滴这套题几乎就是当时互联网公司研发岗笔试的缩影看似常规实则对基础功底和工程思维要求很高。这套题没有太多哗众取宠的偏题怪题反而特别“实在”涉及算法与数据结构、操作系统、计算机网络、数据库等经典板块但考察方式非常贴近业务场景。你把这个卷子吃透了收获的不只是应付一场笔试的能力更是一套完整的计算机基础自测清单。无论你是准备校招的应届生、想跳槽的社招选手还是单纯想检验一下自己基本功的开发者这套题都值得静下心来做一遍。这篇文章我就带大家从头梳理一遍核心考点拆解每一类题背后的考察意图再分享一些答题实战里能直接用的策略。1. 这套笔试题的出题逻辑与考察思维很多人拿到笔试卷子第一反应是赶紧看题、赶紧做这是个大误区。理解出题人为什么这么考比盲目刷题重要得多。1.1 为什么滴滴的笔试题值得反复做滴滴作为典型的出行平台业务场景里有几个非常鲜明的技术特征海量用户高并发请求、LBS位置服务、实时订单匹配、路径规划、支付交易。这些业务特点直接决定了它对研发工程师的基础要求——不是你会写CRUD就行而是必须具备扎实的算法能力和计算机底层素养。2016年的滴滴正处于高速扩张期招聘节奏快、标准严。笔试作为第一道筛选关卡不可能像社招面试那样深挖项目所以它更倾向于用一套覆盖面广、梯度合理的题目快速筛选出“基础扎实、逻辑清晰、代码能力强”的候选人。这套题的价值就在于它的考法非常典型——不考偏门知识专考你在大学课程里学过、但在实际工作中容易被忽略的那些核心概念。值得反复做的另一个原因是滴滴的题量设置和难度梯度很有代表性客观题加编程题的组合模式几乎成了后来几年互联网大厂笔试的标准模板。你做会了这一套再去看其他公司的题目会发现很多思路是相通的。1.2 笔试想筛出什么样的候选人我见过不少同学刷题时特别追求“奇技淫巧”专攻冷门算法、偏门数据结构结果上了考场反而在基础题上栽跟头。滴滴这套题传递的信号非常明确它要的不是“题库型选手”而是“解决问题型选手”。什么叫解决问题型就是你面对一个从来没见过的业务问题能用学过的算法和数据结构设计出清晰、可控、高效的解决方案。比如它问你并发场景下订单状态如何管理本质上考的是操作系统里的进程同步和锁机制它问你城市间最短路径如何计算本质上考的是图论里的最短路算法。出题人考察的知识点本身不超纲但套了一层业务外壳你要能快速把这层壳拆掉看到底下真正的考点。还有一层用意是考察候选人的“工程敏感度”。同样是写一个排序有人写出来是教科书式的标准代码有人写出来考虑了数据量、内存占用、稳定性。笔试时间有限能在短时间内把细节想明白本身就是工程能力的一种体现。2. 算法与数据结构占分最高的硬骨头算法与数据结构历来是大厂笔试的重头戏滴滴这套题也不例外。无论客观题还是编程题这个板块的占比都是最高的也是最容易拉开分差的地方。2.1 那几年常考的题型分布结合2016年前后大厂笔试的规律算法与数据结构的考察主要集中在几个固定的类型上数组与链表的操作、栈与队列的特性、二叉树的遍历、图的最短路径、字符串处理以及动态规划。这些都是《数据结构》课程里的核心内容但笔试不会直接问“什么是二叉树”而是通过一道具体的题目让你在限定时间内完成编码实现。举个典型例子用两个栈实现一个队列这道题在滴滴的笔试中出现频率非常高。表面上考的是栈和队列的特性差异其实考的是你对数据结构内部机制的深度理解——栈是后进先出队列是先进先出要用两个栈模拟出先进先出的效果关键在于“倒腾”的时机。我见过不少人在这道题上翻车不是不会原理而是实现时不注意“什么时候该倒、什么时候不该倒”导致队列的amortized复杂度退化成O(n)。正确的做法是入队时只往stack_in里push出队时如果stack_out不为空就直接pop为空才把stack_in里的元素全部倒进stack_out。这里最核心的优化是“一次倒一批”而不是“每入队一次倒一次”这样才能保证每个元素最多被倒腾两次整体均摊时间复杂度O(1)。class Queue: def __init__(self): self.stack_in [] self.stack_out [] def push(self, x): self.stack_in.append(x) def pop(self): if not self.stack_out: while self.stack_in: self.stack_out.append(self.stack_in.pop()) return self.stack_out.pop()2.2 一道典型的链表题该怎么答链表是笔试的常客因为它既能考察指针操作的基本功又能考察边界条件的处理能力出题成本低、区分度又高。那几年滴滴特别喜欢在笔试题里考察链表的逆序、合并、找环这类问题再往深了走就是链表排序。以最常见的“反转链表”为例这道题看起来简单但真在笔试中写出来有不少人会栽在指针赋值顺序上。反转链表的核心思想只有一句话断链之前先保存后继节点。只要你记住这个原则递归和迭代两种写法都不难推导出来。我后来带新人时发现他们最容易犯的错是在迭代时把当前节点的next改掉结果找不到原来的下一个节点了。所以我在笔试时养成一个习惯凡是涉及指针修改的操作先把待修改节点的后继存下来再进行断链和重连。struct ListNode { int val; struct ListNode *next; }; struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev NULL; struct ListNode *curr head; while (curr) { struct ListNode *next curr-next; curr-next prev; prev curr; curr next; } return prev; }如果你在笔试中遇到两两交换链表相邻节点这类变形题思路也是一样的先画图理清指针的“断”和“接”顺序每一步操作前先保存会丢失的指针。链表题切忌在脑子里空想动手画图是最快的解题方式。2.3 动态规划送分题其实是分水岭动态规划题在滴滴这套笔试题里非常典型。这类题的好处是“怎么考都不过时”它考察的是一种很宝贵的思维模型——把一个复杂问题拆解成重叠子问题并用状态转移方程把它表达出来。那几年笔试中出现频率比较高的DP类型有斐波那契数列、爬楼梯、背包问题、最大子段和、编辑距离等。以“爬楼梯”为例假设你正在爬楼梯需要n步才能到达楼顶每次你可以爬1步或2步问有多少种不同的方法可以爬到楼顶。这题的核心在于理解递推关系f(n) f(n-1) f(n-2)想明白“到达第n阶的最后一步要么是从第n-1阶跨1步要么是从第n-2阶跨2步”就足够了。不过笔试中更常考的是“升级版爬楼梯”——加入代价数组问最小花费。这种变体的核心是状态定义要精确dp[i]表示到达第i阶的最小花费。只要状态定义对了转移方程自然就出来了。我建议大家做DP题时养成一套固定流程先定义状态再推导转移方程然后确定初始化和遍历顺序最后考虑空间优化。这套流程走下来就算题目再变形你至少不会慌能一步步把思路理清楚。3. 操作系统与网络高并发场景下的送命题很多偏应用开发的程序员对操作系统和网络知识不重视觉得日常工作用不到。但大厂笔试几乎必考这块内容因为互联网公司特别是滴滴这种业务形态服务端面临的高并发、高可用挑战底层全靠这些基础理论支撑。3.1 进程线程订单系统的底层支撑操作系统板块的高频考点是进程与线程的区别、线程同步、死锁、内存管理等。滴滴的笔试不会直接问你“进程和线程的区别”而是会结合业务场景出题比如“在订单派发场景中多个线程同时操作一个订单队列如何保证线程安全”。这道题的描述本身并不复杂。多个配送员同时抢单对应到代码层面就是多个线程同时从任务队列里取数据如果没有任何同步机制就可能导致多个线程拿到同一个任务或者任务被重复执行。解决思路一般有几种用互斥锁锁队列、用CAS原子操作、用线程安全的阻塞队列等。回答时如果能顺带说清楚各自的使用场景和性能差异会给面试官留下不错的印象。进程和线程的区别也是个老生常谈的问题不过我发现很多人在笔试时回答得不够准确。简要说就是进程是操作系统进行资源分配的基本单位线程是CPU调度的基本单位同一进程内的线程共享进程的地址空间和资源而进程之间则是相互独立的。一个简单的记忆口诀是“进程是资源分配的单位线程是调度的单位”。3.2 死锁产生的四个必要条件死锁是笔试中出了名的高频考点滴滴这套题里也出现过。考察方式通常是给出四个条件让你判断哪些是死锁产生的必要条件或者给你一个场景让你分析是否可能产生死锁。四个必要条件分别是互斥条件、请求与保持条件、不可剥夺条件、循环等待条件。必须全部满足才会产生死锁所以只要破坏其中一个条件死锁就能被预防。我对死锁这块的建议是不要只背四个条件要能结合代码实际分析。比如“请求与保持条件”说的是一个线程持有一个资源的同时又去申请另一个资源——这在平时写代码时非常常见两个锁嵌套就是典型的例子。如果你在笔试里遇到一道“如何避免死锁”的简答题最稳妥的答法是锁排序保证所有线程按同一个全局顺序获取锁。这个答案能解决绝大多数因竞争多个锁导致的死锁问题。3.3 TCP与UDP接口通信的选型逻辑网络板块的重点基本都集中在TCP/IP协议上尤其是TCP的三次握手和四次挥手、TCP与UDP的区别、HTTP协议的状态码和请求方法等。滴滴的笔试题目比较务实喜欢把网络协议和具体业务场景结合起来考。比如这样一道题在滴滴的客户端和服务端通信中哪些场景应该用TCP哪些场景适合用UDP这就是一道典型的“理论联系实际”的题目。TCP提供可靠的、面向连接的字节流服务适合对数据完整性要求高的场景比如登录认证、订单提交、支付交易UDP是无连接的、不可靠的但头部开销小、传输延迟低适合实时性要求高但可以容忍少量丢包的场景比如司机位置实时上报、用户位置GPS更新、聊天消息中不太重要的提示类消息。三次握手的过程也是笔试常客。很多人会背“SYN、SYNACK、ACK”但更关键的是要理解为什么是三次而不是两次——因为三次握手能防止已失效的连接请求报文突然又传到服务端从而产生错误连接。四次挥手同理核心在于TCP是全双工的每个方向的连接必须单独关闭。4. 数据库与系统设计没写过的坑最致命除了算法和计算机基础滴滴这套笔试还有一个很重要的板块就是数据库和系统设计。这个板块的题目不要求你写多复杂的代码但非常考验工程素养和思维缜密程度。4.1 SQL考察业务场景里的活用数据库方面的题目往往围绕SQL编写、索引机制、事务隔离级别来出。我记得2016年前后很多公司的笔试都爱考察“经典SQL语句编写”——比如找出每个城市订单量最多的司机或者统计某一时间段内的订单总数。这类题目看起来简单但如果平时停留在ORM框架的思维里手写SQL的能力会比较生疏很容易在GROUP BY、HAVING、ORDER BY的配合使用上卡壳。一个比较典型的例子是有一张订单表orders(order_id, driver_id, city_id, order_time)要求查询每个城市最近一周内的订单总量。标准的写法是SELECT city_id, COUNT(*) FROM orders WHERE order_time DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY city_id;这道题的关键是GROUP BY的正确使用以及WHERE和HAVING的区分——WHERE是分组前过滤HAVING是分组后过滤。不少人在笔试题上会混淆这两个关键字的使用场景。索引也是高频考点。一道经典题目是一个SQL查询很慢你如何排查优化完整的回答步骤应该是先看执行计划确认是否走了索引再分析是查询条件字段没有建索引还是索引失效然后考虑是否返回了过多没用到的列能否用覆盖索引优化最后才是考虑业务层面能不能减少查询次数比如加缓存。4.2 系统设计题从匹配到派单的高并发思考系统设计题是笔试中比较有区分度的一类滴滴的题目通常和自身业务紧密贴合。比如让你设计一个“司机乘客订单匹配系统”写出核心流程、用到的数据结构和关键技术方案。这类题不要求你写完整代码而是考察你面对复杂业务场景时的架构思考能力。我当时拆解这类题的思路是这样的先明确系统核心是“匹配”两个字一辆车、一个乘客在空间和时间上达成最优匹配。这个匹配过程的核心数据结构是“按距离维护的司机位置索引”实际工程里地理坐标可以映射到某个key比如geohash并维护在Redis的有序集合里按距离排序快速找到附近的司机列表。接着要考虑高并发场景下的数据一致性问题。订单创建和司机接单必须是一个原子操作否则会出现两个司机同时抢到一单的情况。解决方案是引入分布式锁或者利用数据库乐观锁保证同一时间只有一个线程能成功更新订单状态。如果再深入一些可以谈消息队列削峰、缓存热点解决、服务降级等话题这些都会是加分项。4.3 面向对象设计模式怎么答面向对象和设计模式也是笔试里的常客。滴滴这套题里通常会出现一些关于继承、多态、封装的选择题或者是一道“说出几种设计模式并说明使用场景”的简答题。这类题最容易犯的错是“背概念忘了场景”。比如单例模式很多人只知道“保证一个类只有一个实例”但说不清什么时候真的需要用单例。我自己的经验是回答设计模式题目时一定要绑定“为什么”——线程池、配置管理器、数据库连接池这些就是单例模式天然的使用场景。再比如“依赖倒置原则”它解决的是模块间的耦合问题。滴滴的业务模块划分很清晰很多模块之间都需要解耦而依赖倒置就是通过抽象接口来隔离变化让高层模块不依赖低层模块的具体实现。答题时结合一个实际的业务场景来讲能明显拉开和其他候选人的差距。5. 笔试实战策略时间分配与答题顺序技术实力是笔试的基础但同样实力的人会因为答题策略不同而拉开不少分差。我自己当年参加大厂笔试时踩过不少坑也积累了一些非常实用的策略。5.1 拿到卷子的前10分钟很多人拿到试卷就埋头开始做这是个典型的错误。我建议前10分钟什么都别写先快速把所有题目从头到尾扫一遍做三件事标记题目的类型和难度、估算每道题需要的时间、找出有没有自己熟悉的“送分题”。扫题的逻辑在于笔试的题目分布往往不是按难度递增排的有时候最后一道编程题反而最简单。如果你按顺序答题很可能在一道难题上卡了太久结果最后一道送分题没时间做了。扫一遍卷子把送分题先吃掉把大头分数稳住再去啃硬骨头性价比更高。滴滴这套题的时间压力不小尤其是编程题需要预留出足够的调试时间。我的经验比例大概是选择题和简答题控制在总时间的45%以内剩余55%的时间全部留给编程题。因为选择题就算不会也有25%的概率蒙对但编程题不运行通过就是零分一分都没有。5.2 答题时的隐藏加分项笔试的编程题尤其是线上OJ判分通常只关注结果对不对。但如果是纸质笔试或者半结构化笔试阅卷人的主观印象也很重要。我在给团队筛选简历时看过不少笔试答卷说实话写得规整不规整第一眼印象差别非常大。什么叫规整的代码第一有缩进、有空格变量命名有意义不是a、b、c一溜往下排第二有必要的注释尤其是核心逻辑部分能让阅卷人快速理解你的思路第三代码分段清晰边界条件处理一眼就能看到。还有两个隐藏加分项值得注意一是写代码之前先写思路哪怕只有两三句话能让阅卷人知道你有清晰的解题框架二是在复杂度允许的情况下优先选择实现简单、不容易出bug的方案而不是炫技式的写法。笔试考的是正确率不是代码大赛。5.3 不会的题怎么拿分笔试时遇到不会的题最忌讳的是直接放弃。实际上即使不完全会做也可以用一些技巧拿到部分分数。以编程题为例如果你不会最优解可以先写一个暴力解法至少能通过部分测试用例拿到部分分数。在暴力解的基础上如果你能想到优化方向比如加一维记忆化、用哈希表减少查询时间、用双指针减少遍历次数都可以写进代码的注释里让阅卷人看到你有优化的意识这比交白卷强太多了。对于选择题和简答题遇到不确定的先排除掉明显错误的选项再结合题干里关于业务场景的描述去猜正确率会提升不少。简答题就算只记得零散的知识点也尽量用自己的话组织成一条条清晰的要点千万不要空着不写。6. 常见失分点与备考避坑实录做了这么多年技术面试相关的工作也接触过大量笔试真题和考生反馈我总结出一些非常典型的失分原因。这些坑如果你能提前避开就已经超过了大多数人。6.1 看题不仔细导致的翻车笔试中最冤枉的失分就是“审题失误”。我见过太多人明明会做却因为没看清题目要求而丢分。比如题目要求“保持元素的相对顺序不变”结果用了不稳定的排序题目要求“输出所有可能的组合”结果只输出了一种题目要求“在O(n)时间复杂度内完成”结果写了个O(n²)的解法。我自己的习惯是拿到一道题先把题目的关键条件圈出来尤其是时间和空间复杂度要求、输入数据的范围、是否允许使用额外的空间。这些约束条件直接决定了你能用什么算法、不能用什么算法。滴滴的题尤其喜欢在“输入规模”上设坑比如给出n的范围是10^9其实就是暗示你不能用O(n²)的算法只能用O(nlogn)甚至O(n)的方案。6.2 复杂度估算错误的典型场景复杂度分析是笔试必考也是很多人容易犯错的地方。一个很典型的场景是递归算法的时间复杂度分析比如斐波那契数列的朴素递归很多人会条件反射地写O(2^n)如果题目只问普通情况倒还好但一旦说明经过记忆化优化就是O(n)。这类细节是分水岭。还有一类是“嵌套循环嵌套了个递归”很多人在分析时容易漏算一层。我的建议是遇到任何复杂度分析题先把代码的执行路径画一遍用一个具体的输入规模去试算再抽象成数学表达式。纸上推演永远比脑子里想靠谱。关于空间复杂度滴滴这套题里也经常混着考。记住一个原则递归每一层调用都会占用栈空间所以递归深度是多少额外空间就是多少。迭代解法通常空间复杂度更低这也是为什么很多题目的最优解都是把递归改写成迭代。6.3 备考资料与刷题节奏建议最后说说怎么备考这类大厂笔试。我自己见过太多同学刷题没有章法今天做两道链表明天看两道动态规划过一周发现全忘了这就是典型的“无效刷题”。我的建议是分三个阶段走。第一阶段打基础把大学核心课程的重点过一遍数据结构的所有基础操作能手写稳定排序和不稳定排序有哪些、各自复杂度是多少并查集、堆、哈希表、链表、树的遍历要达到“闭着眼睛能写”的程度。第二阶段专项刷题按数据结构或算法类型分类刷每个专题集中突破比如花三到四天专门做动态规划从简单题到困难题梯次推进。第三阶段做整套题严格计时模拟真实笔试环境重点训练时间分配和心态。资料方面不用贪多把经典教材吃透再配合在线刷题就够了。重点不是量的堆砌而是每道题都搞明白“为什么这么做”和“还能怎么做”这样就算题目穿了一层新马甲你也认得出来。备考是个枯燥的重复过程但回报非常直接。我见过太多基础不错的人死在不是不会、而是不熟的坎上。笔试的时间窗口很短只有把常见题型的解题路径练成肌肉记忆你才能在考场上腾出精力去对付那些真正拉分的难题。刷题时也不要一味求难大量中等题做熟练了效果远比死磕几道难题好得多。