资讯动态

百度2016研发笔试题(三)复盘:从C/C++细节到操作系统与TopK算法

发布时间:2026/8/30 16:13:40 来源:尧图企业网站定制
别被“研发工程师”三个字骗了百度2016笔试题三到底在考什么那年秋天我坐在学校的机房里屏幕上是百度研发工程师笔试的在线答题页面右上角的倒计时跳得人心慌。我记得很清楚当时我自认为准备得还算充分——刷了剑指offer背了各种排序算法操作系统和网络也过了一遍。结果做到第三套题的时候我还是被几道看似不起眼的小题卡住了。不是不会而是那种“明明知道它考什么却拿不准它想要哪种答案”的煎熬比完全不会更难受。后来我入职做研发也陆续参与过几次校招笔试的出题和阅卷站在出题人角度回头看那年的题目才真正理解了那套题的设计逻辑百度2016研发工程师笔试题三并不是在考“你会不会写代码”而是在考你有没有形成一套完整的工程师思维方式。网上很多资料把这套题归为“C/C、数据结构、操作系统、网络”四大块这个归类没错但太笼统了没有说到点子上。这篇文章我按自己的理解把“第三套”这套题的重点题型、考点背后的原理、以及每类题对应的解题思路重新梳理了一遍。如果你是正在准备大厂笔试的应届生或者工作几年想回头补一补底子的开发者这篇内容应该对你有用。1. 2016年百度研发岗笔试的整体命题逻辑为什么“第三套”这么有代表性你要先明白一个前提2016年的互联网校招笔试和现在有很多不同。那会儿没有那么多在线评测平台给你反复刷题笔试题目大多由部门资深工程师手工出题题目风格非常“原始”——直来直去地考基础不玩什么花活。但也正因为如此它反而诚实地反映了大厂筛选研发工程师的真实标准。1.1 题量结构广度优先深度试探回忆一下那套题的结构单选题、多选题、填空题、简答题和编程题都有。单选多选覆盖的知识面非常广从C语言指针、数组、内存布局到操作系统进程调度、死锁再到TCP/IP协议、Linux命令几乎无死角。这种结构本身就说明了一个态度研发工程师的知识面必须够宽不能只知道某个方向的细节。很多人在准备笔试时有一个误区觉得算法题是唯一的胜负手数据结构死磕到底就行了。但你去看那套题纯算法和数据结构的占比并没有想象中那么高。相反C/C的语言细节、内存管理、操作系统原理这些“硬基础”占了相当大的比重。这个设计思路其实和大厂的业务场景直接相关百度那会儿的核心产品线搜索、广告、地图都是高并发、海量数据的后台服务这些系统的稳定性和性能高度依赖工程师对底层机制的理解。一个连数组名和指针都分不清的人谁敢让他去碰检索系统1.2 命题风格逼你“知其所以然”“第三套”里很多题的考法不是直接问概念而是给你一段代码、一个场景让你判断输出或分析行为。比如C语言里经典的指针和数组辨析题给你一个二维数组int a[3][4]问a、a[0]、*(a1)这些表达式的类型和值是什么关系。这类题如果你只是背了“数组名是首地址”这种口诀基本做不对因为题目考的其实是“数组名在什么场合会退化为指针”以及“不同表达式背后的类型语义”。还有一类题也很典型给你一个看似正常的代码片段里面藏着未定义行为或者实现定义行为让你判断问题出在哪。这类题目的背后是一个非常重要的工程素养——能识别出代码中的隐患。笔试阶段就开始考察这一点说明大厂真正想要的不是“能写出能跑代码”的人而是“能写出可靠代码”的人。1.3 那年的“热门考点”背后是真实业务为什么那套题反复出现死锁、进程间通信、内存分配这些内容因为搜索系统的服务端是典型的并发密集场景多个线程同时操作共享资源是常态分布式系统的节点之间要频繁协调。如果你不懂锁的机制、不懂死锁的四个必要条件、不懂IPC的适用场景写出来的服务一上线就可能出事故。所以那套题的每一个考点都能映射到一个真实的生产问题这一点你如果只看试题本身是感受不到的但当你真正进入这个行业之后再回头看会豁然开朗。2. 操作系统与网络死锁、IPC、TCP状态机这类题为什么会反复出现“第三套”里操作系统和网络相关的题比重不小而且不是那种死记硬背的概念题。我挑几个典型题型展开说一下你会发现背后其实是同一套思维逻辑遇到资源竞争、数据传递的问题你怎么选择合理的解决方案。2.1 死锁题不是让你背四个必要条件而是让你找“预防点”那套题里有一道很经典的场景题系统中有多个进程每个进程需要同时持有两个资源才能继续执行问哪种资源分配策略可以避免死锁。选项里有“破坏互斥条件”“破坏请求与保持条件”“破坏不可剥夺条件”“破坏循环等待条件”等。表面上看这是考死锁的四个必要条件但实际出题人的潜台词是你在设计一个多线程程序时知道怎么通过锁的粒度控制来规避死锁吗我个人觉得这类题的最佳解法不是去背那四个条件而是用“资源分配图”去推。把每个进程当节点把资源当节点进程指向资源的边表示“请求”资源指向进程的边表示“已分配”。如果分配图中出现了循环等待的环路那这个环路就是死锁的充分条件。笔试时如果遇到“以下哪种方式能预防死锁”这种题你就在脑子里构建一个简化模型逐一模拟每种策略对环路的影响破坏请求与保持意味着进程必须一次性申请所有资源破坏不可剥夺意味着资源可以被强占破坏循环等待意味着给资源编号、按序申请。这四种思路对应到代码层面其实就是锁的获取顺序、锁的超时机制、锁的粒度设计并不抽象。2.2 进程间通信考的是“场景匹配”那套题里有几种IPC方式的对比题管道、消息队列、共享内存、信号量、Socket。它往往不会直接问“哪种方式最快”而是描述一个具体场景让你选合适的通信方式。比如A进程和B进程在同一台机器上需要频繁传递大量数据选什么答案是共享内存因为它避免了内核态和用户态之间的数据拷贝。管道和消息队列本质上都要经过内核缓冲区效率在数据量大时明显不如共享内存。我见过很多人在这类题上选错原因是用“功能”而不是“性能特征”去匹配场景。你必须在脑子里有一张表管道适合有亲缘关系的进程间少量数据传递、消息队列适合非亲缘进程间结构化消息传递、共享内存适合大数据量高频交互、信号量本身不是通信而是同步、Socket适合跨机器通信。这张表记住了IPC的题基本都能拿分。2.3 TCP状态迁移TIME_WAIT这个点为什么反复被拎出来考网络题里有一道我印象很深的题TCP连接关闭过程中主动关闭方最后进入的状态是什么答案是TIME_WAIT然后它会持续2MSLMaximum Segment Lifetime报文最大生存时间才会进入CLOSED。这个知识点几乎年年考但很多人只是死记答案不理解为什么要有这个状态。TIME_WAIT存在的核心原因是最后一个ACK可能会在网络中丢失被动关闭方收不到ACK就会重发FIN主动关闭方必须留在TIME_WAIT状态以便重发ACK。第二原因是防止旧连接中的延迟报文干扰新连接。理解了这个机制之后你在实际做高并发服务调优时就知道如果服务端主动关闭连接TIME_WAIT状态的连接会大量堆积占用本地端口和内存。解决方案有几种调整内核参数、开启tcp_tw_reuse、或者改由客户端主动关闭连接。但你需要知道tcp_tw_reuse是应对“主动发起连接的一方”的TIME_WAIT复用问题不是万能的用不好会引发连接异常。这道题如果能答到这个深度说明你不仅仅是背会了状态图而是真的理解TCP的可靠性设计思路。3. C/C与数据结构最容易在一道小填空题上翻车的三个考点C/C相关的题是“第三套”的大头也是最容易暴露真实水平的板块。很多人科班出身学了四年C语言结果做这套题依然会在一道看起来人畜无害的填空题上栽跟头。我把最有代表性的三个考点拆开讲。3.1 数组名、指针、指针数组一道题就能筛掉一大半人几乎可以确定“第三套”里有一道类似这样的题int a[5]问a、a[0]、a这三个表达式的值相同吗类型分别是什么如果你回答“都是首地址所以一样”那这道题你就已经丢了。答案其实是a和a[0]的值相同类型都是int*指向数组第一个元素的指针a的值从数值上看也是数组首地址但类型是int()[5]也就是指向整个数组的指针。最关键的区别体现在指针算术上a1会跳过4个字节一个int而a1会跳过5420个字节直接越界到了数组末尾之后。这就是C语言里“值相同、类型不同导致行为不同”的经典陷阱。那套题还特别喜欢在这个基础上叠加二维数组比如int b[3][4]问b、b1、b1、*(b1)都代表什么。你可以这样记数组名在大部分表达式中会退化为指向其首元素的指针但只有作为sizeof、的操作数时数组名才保持“整个数组”的意义。把这一条吃透类似题就能通解。3.2 sizeof和strlen同一个对象两个完全不同的答案“第三套”里有一类题char str[] “hello”分别求sizeof(str)和strlen(str)。答案是6和5。sizeof是编译期运算符算的是变量在内存中实际占用的字节数包括结尾的\0strlen是运行期函数遇到\0就停实际返回字符串字符个数。有一个变体特别容易出错void func(char arr[]) { sizeof(arr); }传进来的数组形参在函数内部其实已经退化为指针了所以sizeof(arr)的结果在64位系统上是8而不是你想的数组长度。很多人问“为什么函数里不能直接用sizeof求数组长度”原因就在这。正确做法是让外部传入数组长度或者用模板推导C里可以用template来保留数组长度信息。这种题目考的其实是你是否清楚“数组作为函数参数时发生了什么”。3.3 结构体内存对齐不是纯理论是影响性能和兼容性的实际问题结构体对齐也是“第三套”里容易出现的原题定义一个结构体包含一个char、一个int、一个short问sizeof(struct)是多少如果是默认4字节对齐结果是12而不是7因为char后面会填充3个字节short前面为了满足2字节对齐会填充1个字节。内存对齐出现的根本原因CPU访问对齐的内存地址更高效某些硬件平台甚至不支持非对齐访问。所以编译器会在成员之间插入填充字节。这在实际开发中的直接体现是——结构体成员顺序会影响内存占用大小。如果成员按从大到小或按对齐要求排列就能减少填充字节。比如char、short、int的顺序改成char、int、short和int、short、charsizeof值可能就不同。这个知识点在开发网络协议解析模块时尤其重要因为协议头通常要求紧凑布局你需要用#pragma pack或者__attribute__((packed))来取消默认对齐。百度的很多底层服务涉及自定义协议这个考点几乎是必然要出的。3.4 快速过一遍数据结构常考主干数据结构部分“第三套”没有太偏的题。树、图、排序、查找、栈和队列都会涉及但考得比较常规。二叉树遍历的变体已知前序中序求后序、堆的建堆和调整过程、散列表的冲突处理方法这些属于送分题不能失分。这里要特别提醒的是别把复习重点放在“手撕红黑树”这种偏冷门的操作上大厂笔试更看重你对常见数据结构的空间时间和适用场景的把握。例如“哈希表和二叉搜索树的查找复杂度分别是多少各有什么优缺点”这种题出现的频率远高于“请你实现红黑树的左旋右旋”。4. 算法设计题TopK问题与海量数据的思考路径算法编程题在“第三套”里占的比重不小压轴题往往是一道让你设计解决方案并写出核心思路的题目。这类题不一定要求提交可运行的完整代码但要求你写出关键的设计思想和伪代码。它的考察重点不是代码能力而是“面对一个陌生问题时你的工程化思考路径”。4.1 从一道海量数据题说开去那套题里有一道非常经典的存在给定一个文件名里面有上亿个整数内存不足以一次性全部载入问如何找出其中最大的100个数这就是“海量数据中求TopK”的典型题。正解是维护一个大小为100的小根堆顺序扫描文件中的每个数字如果当前数字比堆顶大就替换堆顶并调整堆结构。这样单趟扫描即可时间复杂度O(N*logK)K100非常小整个文件只需要在内存里同时保留100个数。这道题其实是在考你对堆这个数据结构的理解和应用深度大根堆可以O(1)拿到最大值但你要维护“最大K个数的集合”反而要用小根堆因为堆顶是最小值它才是集合的“门槛”。如果你用小根堆堆顶就是这100个数里最小的那个新来的数只要大于它就说明它“有资格进群”于是把最小的踢出去。这个逻辑稍微绕一下但想通了就再也不会忘。4.2 衍生如果数据带权重、如果要求不重复、如果分布式同样一道题稍微变一下就能考出不同的能力层次。比如“找出出现次数最多的100个整数”就不能直接用大小为100的小根堆了你得先做分桶或者哈希统计频次再在频次数据上跑TopK。再比如“数据分布在多台机器上每台机器都有一个大文件”那就先每台机器各自求TopK再归并。这就是分布式计算里的MapReduce思想的雏形map阶段在本地求局部TopKreduce阶段合并求全局TopK。我个人建议准备这类题时给自己建立一套完整的“海量数据处理工具箱”哈希分治把大文件切小、位图法处理存在性判断和去重、布隆过滤器允许小概率误判的存在性判断、堆/外部排序求TopK。笔试时看到“内存不够”这四个字就要条件反射地在这些方案里找答案。这不是套路而是因为真实的大数据场景下能用的手段确实就这么几类。4.3 贪心、动态规划、回溯“第三套”常见算法方法百度那套题里的算法题不会刻意考特别复杂的动态规划更多是经典模型。比如求最长公共子序列、最长递增子序列、0-1背包的变体。这类题的通用思考路径是先暴力递归画出递归树看有没有重叠子问题如果有就加备忘录记忆化搜索然后看能不能改成递推的DP表。这个方法虽然朴素但对笔试阶段足够管用因为大部分DP题的第一眼思考方向都是从这里出发的。我见过很多人在笔试时卡在DP的“状态定义”上其实是因为没有建立“按最后一步来思考”的习惯。动态规划的核心就是状态转移方程而状态转移方程大多可以这样推导假设当前最优解包含最后一步那么除去最后一步的前缀也必须在相应的状态下是最优的。你把这个句式套进场景里状态定义自然就有了。5. 复盘与应试建议这套题暴露出来的真实能力短板如果只把“第三套”当作“刷题素材”来做你的收获会非常有限。做完之后一定要花时间复盘搞清楚每一道错题背后暴露的是哪一方面的欠缺。我把这套题常见的“失分点”按能力维度整理了一下你可以对照自检。失分能力维度典型表现对应考点或题目类型补救方向语言细节掌握不深“数组名和指针区别”题做错C/C中数组与指针的语义辨析精读一本C语言原理书重点看数组与指针章节操作系统原理模糊死锁相关场景题拿不准死锁四个必要条件及资源分配图结合多线程锁的使用复盘画出分配图推演网络协议只背状态机TIME_WAIT题只答对名称说不出原因TCP连接关闭过程与状态迁移抓包看一次真实的连接关闭过程结合报文分析数据结构会用不会选能实现排序但不知道场景适用性各类排序、查找、堆、散列表的对比分析整理一张“数据结构选型表”按场景复习算法设计缺工程思维会写递归但不会分析复杂度算法题中要求评估时间空间复杂度刻意练习主定理分析和递归树法实战经验不足不知道共享内存和管道在实际系统中的应用差异进程间通信的各种方式对比用代码实测每种IPC在不同数据量下的性能这个表格是我后来复盘时自己整理的不一定和标准答案一一对应但它指向了一个更基础的规律大厂笔试喜欢考“你能否把大学课本上的理论变成现实系统中的工程直觉”。如果你做这套题时发现自己在“操作系统原理”和“网络协议”上失分很多那你缺的可能不是刷题量而是缺少底层系统编程的实际经验。你可以自己动手写一个简单的多线程服务用管道或共享内存做进程间通信再观察一下TCP连接的状态变化这些实践对理解笔试考点比刷一百道题都管用。6. 应试细节三套题做下来我发现笔试其实有“送分题”和“陷阱题”之分最后聊点实操层面的东西。当年我刷完“第三套”之后总结了一套应试方法分享出来。这套方法不局限于百度对大多数大厂研发笔试都适用。6.1 先扫一遍题把题分成三类再动手拿到试卷我建议大家先用两三分钟把所有题过一遍不要从第一题开始按顺序做。题目实际上可以分为三类送分题看一眼就知道答案、陷阱题感觉会做但容易在细节上出错、硬骨头题一时没思路但分值高。我的策略是先把送分题尽快拿稳再做陷阱题最后留时间给硬骨头。陷阱题最典型的就是sizeof和strlen、数组名和数组名、结构体内存对齐这类——你会觉得考的是同一个知识点但每道题的考点侧重完全不同稍不注意就掉进“我以为它考的是这个”的误区里。6.2 选择题的“最优解”不等于“正确解”笔试选择题里经常遇到“以下哪种方法最优”的提问方式。这时候要特别小心出题人眼中的“最优”不一定是你平时写业务代码时认为的“最优”。它的判断维度往往是性能时间复杂度和空间复杂度和可靠性优先而不是代码可读性。比如问“求TopK最优的方法”小根堆是正解如果你选项里看到“先全排序再取前K个”这在时间复杂度和空间复杂度上都明显更差直接排除。平时写代码时因为数据量小排序法的性能差异根本体现不出来但在笔试这种假设极端场景的题目里你必须用理论复杂度去判断。6.3 编程题的“伪代码”策略“第三套”的编程题大概率不要求你写出完整可运行的代码而是要求写出算法思路。这种情况下不要上来就写代码先用中文把思路分步骤写清楚再配关键伪代码。这样阅卷人在很短时间内就能看出你的设计意图即使代码里有个别语法错误也不至于全扣分。伪代码的核心是体现数据结构的选择和核心逻辑的流程比如“建立一个大小为K的小根堆遍历文件中的每个整数若当前值大于堆顶则弹出堆顶并插入当前值最后堆中元素即为所求”这段写在试卷上比纠结接口怎么定义有意义得多。关于这套题我最后想多说两句做“第三套”已经是很多年前的事了但那套题对我的影响一直保留到现在。这些年我自己也面试过不少人最大的感受是笔试刷题可以熟能生巧但真正让人拉开差距的是把一道题放进真实系统里理解的能力。考死锁不只是让你默写四个必要条件而是让你在设计并发模块时条件反射地思考锁的顺序考TCP状态不只是让你背状态图而是让你在线上服务出现大量TIME_WAIT时能迅速定位问题。你在做这套题时建立的这些思维连接面试官后续聊项目时三句话就能问出来。要说实际的备考建议我个人的体会是2016百度研发工程师笔试题三这类资料的价值不在“押中原题”而在帮你校准自己的知识体系。做完之后如果你发现“C/C指针”和“操作系统进程通信”还得再看一遍那这套题就值了。哪怕离笔试只剩一周也值得把考点涉及的书本章节重新翻一遍配合几道练习题巩固比再开一套新卷子有用得多。毕竟大厂研发工程师的筛选标准从来不是“你刷过多少题”而是“给你一个真实系统问题你从哪里开始思考”。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价