ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

百度研发工程师笔试题复盘:核心考点与解题思路

百度研发工程师笔试题复盘:核心考点与解题思路 考过百度2016研发工程师笔试题二的人掐指一算现在差不多都工作了五六年。这套题在当年的校招圈子里算得上经典覆盖了数据结构、算法、计算机网络、操作系统、数据库、语言基础这些研发岗位的基本盘。现在回看我觉得它最大的价值不在于题目本身而在于它逼着你去把计算机基础重新系统地过一遍。哪怕你不是准备考百度而是想去任何一家做技术的公司这套题对应的知识点都能作为自我体检的清单。这篇文章就是基于这套题做一次复盘不逐题念答案而是把每类问题背后的考点逻辑和解题思路讲明白顺便分享一些我自己做题时踩过的坑以及后来面试别人时发现的高频误区。1. 从一套题看研发岗笔试的选人逻辑1.1 这套题到底在考什么一份典型的研发工程师笔试题题型分布其实很有讲究。从我当年看到的和后来帮公司出题的经验来看数据结构与算法部分通常占了四成以上剩下的空间分配给了计算机网络、操作系统、数据库、语言基础以及少量考察逻辑推理的选择题。也就是说整套题的难度重心在算法但决定你能不能过线的往往是基础部分的正确率。先解释一下为什么算法占比这么高。研发工程师日常做的事情是写逻辑、调接口、处理数据本质上就是在不断做“输入-处理-输出”的设计。算法题考察的不只是你会不会背某个模板而是你能不能把一个陌生问题拆解成已知的数据结构和算法组合。这是工作里每天都在用的能力只是笔试把这件事浓缩到一两个小时里集中验证。基础部分占三成左右考的是网络、系统、数据库这些计算机体系里的共性知识。这部分更偏向“全体研发都应该知道”的底线性要求。值得注意的是语言基础部分的题目不会特别深不会考那种冷门语法反而会围绕内存管理、多线程、指针、异常处理这些工程里容易出错的点来出题。所以整份卷子看下来它其实是在用一套组合拳判断你平时写代码的基本素养。1.2 出题人想筛掉什么样的人有几年校招经验的朋友可能都有感受笔试阶段真正想筛选的不是“谁会的知识点更多”而是“谁在动手写代码的时候脑子是清醒的”。同样一道题有人上来就闷头写写到一半发现思路错了整段代码推倒重来有人先在草稿纸上画两个例子把边界条件标出来再动手效率完全不一样。这套笔试题里的算法题往往不会只考一个孤立的点而是喜欢在“经典题”的基础上加一些小改动比如加一个时间限制、要求原地操作、或者输出结果有特殊顺序要求。这种出题方式的目的就是筛掉那些只会背题的选手。真正理解解法的人不管题目怎么变都能从状态定义、转移方程、复杂度这几个维度重新推导。我后来面试候选人的时候也经常拿这类思路去问给一道看似没见过的题先听对方怎么分析是直接把答案背出来还是从问题定义开始逐步拆解。坦白说这两种人的差距在笔试成绩上可能不明显但在实际工作里的表现会拉开很大距离。笔试筛选的最终目的还是找到后者。1.3 从“二”看题目的递进关系标题里的“二”其实是个重要信号。它说明这是一套系列试卷中的第二场不是第一场。从历年各种招聘的惯例来看第二场笔试一般不会简单重复第一场的思路而是会在难度和综合性上做明显的拉升。具体表现就是第一场可能还会出现一些概念背诵类的送分题第二场里这类题会大幅减少更多是“读一段代码问输出”“给一个场景分析哪里会崩溃”“设计一个方案解决某个数据规模问题”的综合题。这要求你不仅仅是知识点记住了还得能灵活调用。对备考生来说考第二场之前一定要把基础部分再过一遍因为综合题一多基础不牢的人会非常明显地露怯。2. 数据结构与算法笔试题中的硬骨头2.1 动态规划到底在考你什么动态规划是研发笔试的常客出现频率极高。它考察的本质是“用已知子问题的解递推更大问题的解”。很多同学一看到动态规划就害怕其实大可不必。你只需要抓住三个核心要素状态定义、状态转移方程、初始条件。举个例子经典的爬楼梯问题一个人一次可以上一个台阶或者两个台阶问爬到第n个台阶有多少种不同走法。我们定义dp[i]表示爬到第i个台阶的方法数那么显然dp[1]1dp[2]2dp[i]dp[i-1]dp[i-2]。这就是一个最简单的动态规划模型。笔试里很多看似复杂的题剥掉外壳之后底层都是类似的状态递推。我当时做这类题的经验是不要急着写代码先在草稿纸上把状态定义写清楚。状态定义对了转移方程基本顺理成章状态定义错了后面全盘皆输。这个习惯后来在工作中也很受用遇到复杂业务逻辑第一步永远是先定义清楚数据和状态再谈实现。2.2 一题多解Top K问题推演笔试题目经常会出“一题多解”的经典问题比如Top K问题海量数据里找出最大的K个数。最直接的做法是把所有数排序取前K个复杂度是O(n log n)。但如果你能想到用堆来维护一个大小为K的最小堆每次只和堆顶比较复杂度就能降到O(n log K)。如果再往深处想面试官还可能追问“数据量大到内存装不下怎么办”这时候就需要分治法把数据切分到多台机器每台机器先求局部Top K再汇总。这种递进式的追问方式在笔试题里也有体现只是形式上可能是几道互相有联系的题目。备考的时候我强烈建议把经典题从暴力解法到优化解法都过一遍不要只看最优解。因为你只有理解了从简单到复杂的演进过程才能真正理解每一步优化的意义。直接背最优解的效率很低碰到没有见过的变体题就会卡壳。我曾经在练习时把Top K问题的所有解法都手写了一遍暴力排序、冒泡K轮、最小堆、快排partition、分治归并。写完之后对时间复杂度和空间复杂度的理解明显上了一个台阶。做题真的不能只看答案要亲手推一遍复杂度才会变成自己的东西。2.3 从数据规模反推解法复杂度做题的时候复杂度的估算非常重要它直接决定了你能不能设计出可行方案。很多题目的数据范围会暗示你解法n小于等于10的时候你可能可以暴力枚举n到1000了O(n²)勉强能过n到10万就需要O(n log n)级别的解法n到了百万级基本只能考虑O(n)或O(1)的解法。我做个简单的对照表方便大家对照数据规模大致可接受的复杂度常见解法方向n ≤ 10O(n!) 或 O(2^n)暴力递归、全排列、状态压缩n ≤ 1000O(n²)双层循环、朴素DPn ≤ 10^5O(n log n)排序、堆、二分、分治n ≤ 10^6O(n) 或 O(1)哈希、双指针、前缀和、贪心我见过不少同学做题从来不估算复杂度代码写出来很完整但放到测试数据上直接超时。这不是代码能力的问题是分析能力欠缺。笔试现场时间有限建议拿到一道题先花一分钟看一下数据范围在心里初步估算一个可接受的复杂度再据此选择算法方向。这个习惯养成之后对工作里的性能优化也有很大帮助。2.4 树与链表递归思维是分水岭树和链表的问题在笔试里几乎是必出的而且特别喜欢考递归。二叉树的三种遍历、求二叉树深度、判断两棵树是否相同、反转链表、合并两个有序链表这些都是基础中的基础。但就是这些“基础题”每年都能刷掉一大批人。递归的思维方式其实很简单你要处理一棵树的问题先想清楚根节点要做什么然后递归去处理左子树和右子树。比如求二叉树的最大深度你可以定义如果节点为空深度为0否则深度等于左子树深度和右子树深度中的较大值加1。代码只有几行但背后是完整的递归思维。我做这类题时踩过最大的坑是忽略了空节点判断。很多递归代码写得行云流水但一遇到空树就报空指针异常。笔试的测试用例里空树、单节点树、只有左子树或右子树的这些边界情况出现概率非常高。所以每次写完树相关的代码我都会在草稿纸上把空节点的情况手动推一遍。这个习惯让我在笔试里避免了很多无谓失分。3. 计算机网络与操作系统基础不牢容易翻车3.1 TCP协议从握手的细节到工程的痛点研发岗位笔试里计算机网络几乎必考TCP协议。三次握手、四次挥手、为什么主动关闭方要进入TIME_WAIT状态、TCP和UDP的区别这些问题翻来覆去地出现。你可以把它理解成打电话的过程拨号、接通、通话、挂断。三次握手保证双方都能确认对方的收发能力正常同时同步初始序列号四次挥手则在双方各说一次“我要挂了”并且得到对方确认之后才真正断开。TIME_WAIT是个容易被忽略但又很重要的细节。主动关闭连接的一方在发送最后一个ACK后还要等待2MSL时间才能完全关闭连接。这样做是为了防止最后一个ACK丢失让对端重发FIN时自己还有状态可以响应。实际工程中高并发短连接场景下会出现大量TIME_WAIT状态的连接这也是经常需要调优的痛点。笔试考这个点其实是在考察你有没有真正理解协议设计背后的可靠性考量。另外HTTP状态码也属于高频考点。200、301、302、304、400、403、404、500、502、503这些常见状态码的含义必须烂熟于心。特别是301和302的区别301是永久重定向302是临时重定向涉及到搜索引擎收录和浏览器缓存行为理解起来需要结合具体场景。3.2 操作系统进程、线程与死锁操作系统部分的常见考点集中在进程与线程的区别、死锁的四个必要条件、虚拟内存与页面置换算法。进程是资源分配的基本单位线程是CPU调度的基本单位。一个进程内的多个线程共享进程的地址空间所以线程间通信成本低但同步问题也更突出。死锁的四个必要条件分别是互斥、持有并等待、不可剥夺、循环等待。笔试常考的是给一个场景问你如何破坏其中一个条件来避免死锁。比如在代码中使用锁的顺序总是保持一致本质上就是破坏循环等待条件。理解死锁的四个条件不只是为了应付考试在真实的多线程开发中分析线上线程卡死问题时第一反应就是排查是不是有锁竞争导致的死锁。页面置换算法也是常客FIFO、LRU、LFU这些必须能说出区别。LRU最近最久未使用在实际工程中用得最多比如Redis的内存淘汰策略里就有LRU的近似实现。笔试不会直接问“Redis怎么实现LRU”但会从操作系统层面考你的基础理解然后面试环节再往工程方向延伸。基础概念之间是要互相串起来的。3.3 数据库索引与事务的底层原理数据库考点中索引几乎是必考项。你要清楚MySQL InnoDB的默认索引结构是B树它把数据按主键顺序组织在叶子节点上查询时只需要沿着树往下走就能找到目标数据附近的位置不需要全表扫描。之所以用B树而不是二叉搜索树或者哈希表是因为B树的层级更少读写磁盘的次数更少而且叶子节点之间用指针串联做范围查询非常高效。事务的ACID四个特性也是经典考点。一致性、原子性、隔离性、持久性每个特性背后都对应着具体的实现机制。比如原子性依赖undo log持久性依赖redo log隔离性依赖锁和MVCC。笔试里常有一类题问你两个事务并发执行时会出现什么问题脏读、不可重复读、幻读分别对应哪个隔离级别。隔离级别脏读不可重复读幻读读未提交可能可能可能读已提交不可能可能可能可重复读不可能不可能可能InnoDB通过间隙锁基本解决可串行化不可能不可能不可能这些知识点一定要能默写出来也要能解释清楚底层实现。我当时复习的时候把事务、锁、MVCC、日志机制串成一条线来理解发现比孤立背概念要牢固得多。4. 语言特性与编程实践从代码题看工程素质4.1 C里的经典考点指针、引用与虚函数如果试卷里大量题目用C出题那指针、引用、内存管理一定是重头戏。指针和引用的区别这种题看似基础实际上很能看出一个人是不是真的写过代码。指针是一个变量存的是另一个变量的地址可以被重新赋值引用是别名一旦绑定就不能再改变。理解了这层区别再去理解函数的传参方式、返回值方式就顺理成章了。C里的虚函数也是高频考点。虚函数通过虚函数表实现动态绑定这是多态的底层机制。有些选择题会故意给出一个对象的创建和销毁顺序问构造函数和析构函数的调用序列。如果对对象的生命周期没有整体把握很容易在这种题目上踩坑。实际上手调参太多之后你会发现这类问题在工程中经常以内存泄漏、重复释放的形式暴露出来。我当时复习C部分的经验是不只看语法而是去看编译器和运行时背后做了什么。比如虚函数表在内存里长什么样、构造一个派生类对象时基类部分怎么初始化。这些底层细节虽然在日常业务开发里不常直接操作但出了问题调试的时候能省下大把时间。4.2 程序阅读题的陷阱识别笔试题里的程序阅读题往往比算法题更阴险。它不一定难但经常设置一些“陷阱”比如数组越界、整数溢出、空指针。常见的考核点是让你判断一段代码的输出结果或者指出哪里有问题。这类题目考查的是代码审查能力。我特别想强调整数溢出问题。一个int在32位下最大能表示2的31次方减1一旦运算结果超过这个范围就会发生上溢或者下溢。很多看似正确的逻辑在极端输入下就会突然出错。平时写代码养成检查边界条件的习惯其实也能在这种程序阅读题上占到便宜。我建议大家在准备笔试的时候专门整理一个“陷阱清单”把容易犯的错误记录下来循环条件少写等号、数组下标从0开始还是从1开始、除法运算的除数为0、字符串末尾的\0等等。这类问题出现频率极高而且往往就藏在最基础的代码里。4.3 海量数据场景题海量数据处理是笔试里的一道分水岭也是区分“会写代码”和“能解决实际问题”的题目。常见的手段包括哈希分治、位图、布隆过滤器、堆排序、外排序。核心思路是控制海量数据所消耗的时间与空间复杂度把数据规模降低到机器可以处理的程度。举个例子一个经典的场景题几十亿个URL中找出出现次数最多的前100个。你不能把所有数据装进内存也不能直接全排序。常规思路是先用哈希函数把所有URL映射到若干个小文件中每个小文件数据量变小可以载入内存统计频率再在每个小文件里取Top 100最后全局归并。这种题目考察的就是你有没有系统性的工程思维而不只是单纯的数据结构知识。布隆过滤器也是场景题里的常用工具它用来判断一个元素“一定不存在”或者“可能存在”。它的原理是使用多个哈希函数把一个元素映射到位数组的多个位置查询时如果任何一个位置为0就说明元素一定不在集合里。这个“以允许小概率误判换取极低内存占用”的思想在很多系统设计里都会用到。5. 笔试实战的避坑经验与时间分配5.1 我的做题顺序与时间分配策略考试时间有限合理的做题顺序能直接影响分数。我个人的建议是先快速扫一遍所有题目把会做的、有把握的题按顺序先做完尤其是基础部分的选择填空题它们往往是拿分效率最高的。然后再一鼓作气啃算法题最后留出时间检查一遍边界条件。我常用的时间分配方案是这样的题目类型建议耗时策略基础选择题20-30分钟快速判断不确定的先标记不恋战程序阅读题15-20分钟逐行读代码重点看边界条件和循环终止算法题简单10-15分钟/题先写暴力解保底再优化算法题困难20-30分钟/题尽量写思路和部分分不要空白检查时间10-15分钟重点复查边界条件、答题卡格式不要在不会的题目上耗太久。很多同学喜欢死磕一道难题结果花了半小时才发现方向不对后面会做的题都没时间做。笔试的目标是总分最大化不是每道题都要满分。有些基础题可能一分钟就出答案有些算法题可能要二十分钟做性价比高的题才是关键。5.2 高频失误场景与排查技巧我整理了几个笔试中高频出现的失误场景大家可以对号入座第一个是读题不仔细。题目要求输出“下标”结果很多人输出“值”。这种情况在在线笔试里特别常见因为样例输出可能没有覆盖到这种细节一提交才发现全错。解决办法很简单动笔前用30秒把题目要求完整读一遍尤其是输出格式说明。第二个是不做复杂度分析直接用了暴力解法导致大数据集超时。这个问题在平时刷题时就要养成习惯每次提交前先看一眼数据范围心里默念一遍复杂度是否可接受。第三个是边界条件漏判比如空数组、单元素数组没有单独处理。这种问题通过“先写测试用例再写代码”的方式可以有效规避。我在笔试时通常会在草稿纸上写下两个测试用例一个最简单一个带边界条件写完代码后逐行走一遍。第四个是代码写了很长但逻辑中间断了最后调试时把自己绕晕了。解决办法是在写复杂逻辑之前先用注释把步骤列出来每一小段只做一个事。这样代码不容易乱也方便检查。5.3 备考建议刷题不是比数量最后聊聊备考这件事。刷题肯定是绕不开的但刷题的方式比题量更重要。我建议每做一道题把题目归类、总结解法模板隔三天再重新写一遍看自己能不能不查任何资料就写出来。这个“间隔重复”的策略比盲目刷一百道新题要有效得多。再有就是练习手写代码。笔试环境各异有的只能用在线编辑器有的则需要纸笔。不管哪种环境你都应该习惯在没有IDE补全、没有编译器报错的情况下写出基本正确的代码。平时练习的时候可以刻意关掉自动补全多在纸上画流程、写关键路径。这个过程很枯燥但对你考场状态的稳定帮助非常大。我还建议准备一个错题本但不是抄题目而是记录“错因”。比如“递归忘记写终止条件”“循环变量j不小心写成了i”“数组开小了”。这些错因比正确答案本身更有价值。考前几天翻一遍错题本比临时刷十道新题更管用。说实话这些年我面试过不少新人也帮公司出过几轮笔试题回头看百度2016研发工程师笔试题二这类经典试卷最大的感受就是基础知识的扎实程度真的会在笔试和面试的每一个环节里体现出来。你当年背过的每一个状态转移方程、理解的每一次握手挥手、亲手调过的每一段内存泄漏代码最终都会变成你的工程直觉。好好啃下这套题涉及的每一个知识点对你后续的职业生涯是非常划算的投资。
返回列表