资讯动态

百度2016研发工程师笔试题复盘:覆盖算法、OS与C++核心考点

发布时间:2026/8/30 11:54:49 来源:尧图企业网站定制
已经过去这么多年百度2016年的研发工程师笔试题一依然在不少技术社群里被反复翻出来。很多人在牛客网、CSDN或者GitHub的面经仓库里刷过这套题它看起来只是“一套选择题为主、夹杂少量编程题的笔试卷”但真正刷完一遍之后你会发现这套题几乎把所有研发岗必须过一遍的基础盘都盖住了算法、数据结构、操作系统、网络、C、面向对象样样都有。我当年刷这套题时是抱着“查漏补缺”的心态去的结果被自己的知识盲区敲了一记闷棍很多知识点上课时觉得自己懂了真拿到题里一做就露馅。这篇文章我会按这套题的常见考点和出题逻辑来复盘把选择题背后的原理、算法题的思路、C和OS里容易踩的坑逐个拆开讲。无论你是正在准备校招的应届生还是工作几年想回头补基础的开发这篇文章都能当一份“带注释的复习提纲”用。1. 这套题到底在考什么1.1 试卷结构与考察逻辑2016年的这套笔试题一整体上分成两个大部分一部分是客观选择题覆盖数据结构、操作系统、计算机网络、C语言特性另一部分是算法和逻辑题需要通过手写代码或者给出详细的解题思路来回答。从我在牛客网和一些考研论坛上看到的回忆版来看题目总数在二十道上下选择题占了大头最后会给一两道需要动笔的算法题。很多人一看“一套旧题”就不屑于刷觉得几年过去题型早变了。但实际上大厂笔试的考察框架非常稳定。算法题永远是链表、树、栈、队列、动态规划选择题永远是进程线程、内存管理、TCP、虚函数、指针。变化的只是包装场景和选项细节。2016年的题目尤其典型它没有特别出格的偏题怪题几乎所有题目都属于“基础概念一两步推理”的组合能在规定时间内把正确率稳定在八成以上说明基础已经相当扎实了。1.2 为什么2016年的题现在还能刷有一个很现实的原因现在的笔试题库虽然越来越大但很多新题的题源就是早年这些经典题。面试官喜欢把旧题改个参数、换层壳内核一点没变。比如栈的出栈序列、二叉树的前中后序遍历还原、哲学家就餐问题的变体我在后几年的面试和带人过程中几乎每年都能见到。另一个原因是这套题的难度梯度很合理。它的选择题不是“一眼看穿”的送分题也不是“绞尽脑汁也猜不出来”的炫技题。每道题基本都给了你三到四个选项其中故意放了一两个特别像正确答案的干扰项。这种设计特别适合用来训练“对概念的精确理解”——你不但要知道某个知识点是什么还得知道它什么时候是对的、什么时候会悄悄变成错的那个。这种能力恰好是实际开发中最需要的。2. 算法与数据结构送分题和送命题2.1 栈和队列出栈序列与单调栈栈和队列是笔试题的常客几乎每套试卷里都会有一道和“出栈序列”相关的题。题目一般长这个样子入栈顺序为1、2、3、4、5下列哪个出栈顺序是不可能实现的这类题我给个笨办法也是我后来教给周围人的办法不要一个选项一个选项去模拟直接把每个选项按“入栈严格递增”的规则去验证。规则就两条出栈时比栈顶大的元素一定已经入栈比栈顶小的元素必须是当前栈顶到栈底的顺序。换句话说如果某个元素在出栈序列中出现在另一个比它小的元素之前那么它在入栈时一定先于那个元素入栈但它又不能在入栈顺序上违反规律。当年很多人在这种题上失分不是不会栈的原理而是一道题模拟太久浪费了大量时间。后来我养成一个习惯遇到出栈序列题先从答案里找“第一个元素最小/最大”的极端情况往往能快速排除两个错误选项。比如入栈序列1到5出栈序列第一个如果是5那说明五个元素全入栈了之后只能按5、4、3、2、1的顺序弹第一个如果是1那就是入一个弹一个后面元素要按规则判断。2.2 二叉树遍历与递归转迭代二叉树的前序、中序、后序遍历绝对是这套题的重头戏。我记得有一道经典题是给一个二叉树的前序遍历序列和中序遍历序列要求推后序遍历。这道题考的不只是“会做”更考“会不会利用前序找根、利用中序分左右子树”的递归思想。我建议所有准备笔试的人哪怕你现在已经工作了都重新动笔写一遍“递归转非递归”的三个遍历。中序遍历的非递归写法是最容易出错的因为它需要用一个指针做“沿着左子树一路压栈”的动作弹栈之后再转向右子树。很多人写着写着就把“转向右子树”写成了“压右子树然后继续压左子树”导致出现了重复访问。另外要注意边界条件根节点是空指针、只有一个节点、所有节点都只有左子树或者只有右子树。笔试的算法题不会只有正例它的测试用例一定会包含这些边界情况。我自己刷题时会把树相关的代码写在白纸上模拟跑几组数据写完之后再看一遍有没有“空指针解引用”的风险。这个习惯到现在写生产代码都还在用。2.3 动态规划与贪心怎么区分这套题的算法大题里经常出现一个“最大子数组和”或者“最长公共子序列”的原型。题目本身不难但很能看出你是真的理解DP还是只会背模板。我见过不少人在这一类题上分不清贪心和动态规划。简单区分贪心是每一步都做当前看起来最优的选择不能回头DP则是把问题拆成重叠子问题把中间结果存下来之后不断复用。最大子数组和这样的一维DP状态转移方程为dp[i]max(nums[i], dp[i-1]nums[i])本质上就解决了两件事要么从当前元素重新开始要么把当前元素接到之前的最大和后面。你在纸上画几个例子就会发现这个转移逻辑是“最优子结构”的体现。笔试里还有一类很容易失分的题是“硬币找零最少个数”。这种题有人会用贪心去解但贪心只有在硬币面额满足特定条件时才成立。比如面额是1、5、11要找15贪心会先拿一个11再拿四个1一共5枚但正确的最优解是三个5只要3枚。这就是为什么笔试考官喜欢用这种题来试探你对DP到底有没有真正的理解。3. 操作系统与网络选择题里面的拦路虎3.1 进程线程、锁和死锁操作系统部分对很多刷题的人来说是“软肋”因为平时写业务代码很少直接碰这些概念但笔试偏偏特别喜欢考。2016年的这套题里进程和线程的区分是必考的而且一般会从这几个角度来出题谁拥有独立地址空间、谁可以共享全局变量、线程调度开销小在哪里、进程切换为什么比线程切换慢。我总结了一个方便记忆的口诀进程是资源分配的基本单位线程是CPU调度的基本单位同进程下的线程共享地址空间、文件描述符、信号处理函数但每个线程有自己的栈、寄存器和程序计数器。凡是在选项里说“线程拥有独立地址空间”的直接排除。死锁这道题也几乎是固定出场。四个必要条件互斥、持有并等待、不可剥夺、循环等待。选择题经常把事情倒过来问比如“破坏循环等待条件就能破坏死锁”对不对答案是肯定的要么给资源编号、按序申请要么一次性申请全部资源。但要注意“互斥条件”在很多场景下是不能破坏的因为资源的互斥性是由使用场景决定的不是写代码想改就能改。3.2 内存分配与页面置换内存管理这块选择题爱考的是分页和分段到底有什么区别、虚拟内存是靠什么支撑的、页面置换算法哪个缺页次数最少。我当年总把分页和分段弄混。后来我用一句话记住分页是系统为了管理物理内存而产生的对程序员透明页面大小固定分段是程序为了满足逻辑模块而产生的段大小不固定程序员能看得到。从地址转换来看分页用页号和页内偏移分段用段号和段内偏移。页面置换算法里LRU和FIFO是最常见的两个比较对象。LRU的理论缺页率通常比FIFO低因为LRU利用了局部性原理——最近访问过的页面短期内大概率还会被访问。但注意笔试里经常给一个很小的访问序列让你手动模拟两种算法的缺页情况这时候LRU并不一定永远比FIFO少如果序列里恰好频繁访问“刚被换进来”的老页面FIFO可能碰巧表现更好。刷题的时候别背结论老老实实把过程画出来。3.3 TCP握手、拥塞控制与HTTP状态码网络部分的经典三连问TCP三次握手为什么是三次、四次挥手为什么是四次、TIME_WAIT出现在哪一端。三次握手的核心原因是防止“已失效的连接请求报文段突然又传到了服务器”如果没有第三次确认服务器会白白建立连接、分配资源。四次挥手比三次握手多一次是因为TCP半关闭的特性一端发送FIN表示我这边不再发数据了但还可以收数据另一端先回ACK表示我收到你的FIN然后再发送自己的FIN表示我也要关了。HTTP状态码的考察也是重头戏。2xx是成功3xx是重定向4xx是客户端错误5xx是服务端错误。细节陷阱在于301和302的区别301是永久重定向302是临时重定向浏览器对301的缓存策略和对302完全不一样。还有403和404的区别403是服务器理解请求但拒绝执行404是资源不存在。4. C基础和面向对象这里全是细节4.1 指针引用与内存三大问题C类型的题在这套卷子里占比很高而且往往不是考语法本身而是考生命周期和内存布局。有一个比较典型的题目是问“引用和指针的区别”或者“sizeof(指针)是多少”这类题看似基础踩坑率极高。指针和引用的关键区别引用必须在定义时初始化、一旦绑定不能改指其他对象、没有空引用指针可以随时指向其他对象也可以为空。所以“用一个引用去接收一个临时变量”行不行答案是“非常量引用不行常量引用可以”因为非常量引用会阻止临时变量的生命周期延长编译器直接报错。内存问题无非是三大类悬空指针、内存泄露、数组越界。其中悬空指针最隐蔽尤其是“返回指向局部变量的指针”“delete之后没有置空”这两种情况。写选择题时看到“delete后指针自动变成空指针”这种描述一定要打上大大的叉。4.2 static、const、volatile这些修饰符C选择题里static和const的考察密度非常高。static在类里修饰成员变量表示所有对象共享一份数据必须在类外初始化static修饰成员函数表示该函数不依赖具体对象且只能访问静态成员。const修饰成员函数表示该函数不会修改对象状态而一个const对象只能调用const成员函数。volatile是另一个高频考点。它的作用是告诉编译器不要把这个变量优化到寄存器里每次使用都从内存重新读取。实际开发中被外部中断服务函数修改的全局变量、多线程间共享的硬件寄存器标志位都需要加volatile。但要注意volatile不能保证线程安全它只解决“编译器优化导致读取不到最新值”的问题。这四个修饰符单独考都不难难的是排列组合static const、const static、const成员函数static成员变量、volatile指针和指针指向volatile。重点就是注意修饰的是“指针本身”还是“指针指向的对象”——int const *p和int *const p是两个完全不同的声明这种题目基本每年都会出现。4.3 继承多态与虚函数表面向对象部分一定会考多态的实现原理。知道“virtual关键字让函数实现动态绑定”只是最低要求更重要的是理解虚函数表。每个包含虚函数的类在编译期会生成一张虚函数表表里保存的就是虚函数地址对象内存里第一个指针vptr指向这张表。当用基类指针调用虚函数时实际调用的是运行时对象类型对应的虚函数表里的函数地址。有一道经典题是“构造函数和析构函数里调用虚函数会怎么样”。答案是构造函数中调用虚函数不会发生多态调用的还是当前类自己的版本析构函数同理。原因是构造对象时先构造基类部分此时还没有派生类成员虚函数表也还处于基类阶段析构时先析构派生类部分再析构基类部分等执行到基类析构函数时派生类信息已经没了。5. 实战复盘我刷这套题踩过的坑5.1 时间分配与答题顺序建议这套题的选择题数量不少纯做题可能三十分钟就能完成但如果要把每一道题都当成一次“查漏补缺”的机会我建议至少留出一个半小时。我的刷题方式是先不做任何笔记快速过一遍把不确定的题目标记出来第二遍再单独处理这些标记题最后再回头分析为什么第一次会犹豫。做题顺序上我建议先做算法题再做OS和网络选择最后做C部分。算法题需要大块时间和清晰的思维状态放在选择题前面写能避免到了后半程体力下降导致丢分。C题再怎么说也是选择题可以先靠直觉选一遍回头再仔细推敲。5.2 失分重灾区清单根据我刷完这套题以及帮别人复盘的经验失分点集中在这么几个地方失分点常见原因应对策略出栈序列判断错误只凭感觉模拟不写规则用好“先入先出约束”逐项验证二叉树递归转非递归中序和后序的入栈时机混淆用栈状态机画三遍记住“左、根、右”进程线程概念混淆忽略“地址空间”和“调度单位”的区别背口诀进程管资源线程管运行死锁条件记不全只记住三个就以为万事大吉写代码时主动检查四个条件TCP握手次数原理机械记忆“三次”不理解为什么要三次模拟“两次握手导致资源浪费”场景虚函数调用时机不知道构造/析构中的动态绑定失效明确对象生命周期各阶段的状态const和引用细节混淆“指向的对象”和“指针本身”用右左法则逐层解析类型声明5.3 把一套题提炼成自己的知识模板刷完一套题最重要的产出不是“我做了20道题”而是一份属于自己的知识清单。拿这套题来说我会在题目旁边标注它属于哪个知识域然后给每个知识域再补充一道自己找的拓展题。这样处理完一套题等于把整个知识树重新梳理了一遍。比如看到二叉树遍历题我就在旁边写“还需要再看已知中序后序还原二叉树、Morris遍历、层序遍历的锯齿形输出”。看到进程线程题就写“还需要再看协程与线程的关系、线程池的参数设计、锁的几种实现”。这套题就像一张地图它告诉你地图上哪些格子已经被考遍了剩下的格子要靠你自己去点亮。6. 写在最后面试题只是入口这套2016年的笔试题我后来带了几届新人反复拿出来当摸底卷子用。每次有候选人问我“刷这套题有没有用”我都是同一个回答有用前提是你别把它当成题库而是当成一面镜子。我在实际带团队的过程中见过太多能把LeetCode刷三遍、但一问线程安全的本质就卡壳的候选人。反倒是那些愿意在这套2016年“老题”上慢慢磨的人往往能把基础概念和工程实践串起来。就像当年我自己刷到“C构造函数不能调用虚函数”这道题时第一反应是“这题好偏”后来真正在重构一个基类时才发现如果基类构造函数里偷偷调用了虚函数排查起来真的会让人极其崩溃。如果你正准备校招或者跳槽我的建议是把这套题放进一个三天的学习计划里第一天完整做一遍第二天把错题对应的知识点全部展开细看第三天再拿一套同类型的新题做检验。不要贪多一套题吃透比草草刷十套题管用得多。等你有一天面试别人翻开这套题能轻松说清楚为什么每个选项是对的或者错的那说明你的计算机基础已经真正站稳了。

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

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

免费获取报价