大厂研发笔试真题拆解:算法、操作系统与网络核心考点详解

发布时间:2026/8/30 10:15:11
大厂研发笔试真题拆解:算法、操作系统与网络核心考点详解 1. 这套题到底在考什么如果你准备过国内大厂的研发岗笔试应该对“百度2016研发工程师笔试题四”这套题有点印象。它属于典型的校招/社招笔试套题虽然年份稍早但考查的知识框架和出题风格到现在仍然很有代表性。很多后来流传的所谓的“大厂题库”底层都是这一批题演化出来的。这套题覆盖的范围很明确算法与数据结构、操作系统、计算机网络、数据库、C/C/Java语言基础、设计模式最后还有一道手写代码题。和现在很多厂笔试题目动辄“系统设计”相比这套题更偏向计算机基础功底的测试属于“基本功大阅兵”。我看到很多人在刷这套题时第一反应是“知识点太杂了不知道从哪里下手”。其实换个角度看它恰恰是一份很好的“体检报告”能让你快速发现自己在哪个板块存在盲区。我当年刷这套题的时候操作系统那一块错得最惨后来针对性地补了进程线程和内存管理再回头做其他厂的题明显顺手很多。这篇文章我就以这套题为引子不逐题报答案而是把几类核心题型的解题思路、背后考察的知识点、以及我在实际做题和面试中踩过/见过的坑系统拆一遍。你把它当“解题方法论”看比死记答案有用得多。2. 先分清题型选择、填空、编程各有什么脾气2.1 选择题不是你认识选项就能做对这套笔试题的选择题占了大头我在实际做题时发现它的选择题有一个明显特征每个选项不是一个孤立的知识点而是四个不同的“陷阱”。比如考到TCP的时候它会把“三次握手”和“四次挥手”混在一起考到二叉树的时候会把前序、中序、后序遍历的结果放到一起让你分辨。你如果只是“知道”这些概念但做不到精确记忆非常容易栽进去。我建议的选择题策略是当做判断题来刷每个选项都问自己一句“为什么对 / 为什么错”。如果某个选项你说不出原因那它就是你的薄弱点立马翻书不要拖。举一个高频考点来说进程和线程的区别几乎是必考。选择题里常见的干扰项包括“线程拥有独立的地址空间”——这就不对线程共享进程的地址空间“进程切换的开销比线程小”——这就不对反而是线程切换轻量一些“一个进程崩溃不会影响其他进程”——这通常是对的因为进程间地址空间隔离。这种题你要不是真的理解“PCB、TCB、资源共享、调度开销”这些底层概念光靠背选项换一套卷子照样错。2.2 填空题精确术语别写“约等于”这套题里有一些填空性质的题目虽然数量不多但它们要求你写出精确的数据结构名称、算法名称、或者某个操作的时间复杂度。这里得分的关键就一条术语要精确描述要严谨。比如让你填“用数组实现栈和用链表实现栈在入栈操作上的时间复杂度分别是多少”正确答案是O(1)和O(1)。但很多人在链表实现那里会犹豫觉得“链表还要分配节点不是O(1)吧”。这就是把“均摊”和“单次最坏”混淆了。链表分配节点如果只考虑算法本身确实是常数时间如果你考虑内存分配器的开销那另说但笔试题默认不讨论这个。填空题的另一个坑是结果是对的但推导过程写得不清不楚。阅卷时如果只看最终答案还好但如果有人工复核过程混乱很容易丢分。我自己的习惯是先在草稿纸上把过程写完整再往卷面上誊写一遍精简版。2.3 编程题不只考“能不能写出来”还考“能不能写好”这套题最后会有一道编程题常见类型是链表操作、二叉树遍历、或者简单的动态规划。题目本身看着不难但评判标准很严格解出来了不一定满分还要看你的边界处理、代码风格和复杂度。我记得当年做一道“反转链表”的题目自己明明写出来了但漏掉了“链表为空”和“只有一个节点”两个边界判断结果只能过一半的测试用例。后来我学到一个习惯写任何链表/树的代码第一件事就是写边界条件哪怕题目没说你也要主动考虑。编程题这里我建议你刻意练习“白板编码口头解释”的组合。很多人在IDE里能写出来但在笔试的在线编辑器里没有自动补全、没有编译器提示就会大脑空白。这类场景下最靠谱的方法是先画图再写码先写注释再填实现。3. 算法与数据结构笔试里的“硬通货”3.1 排序与查找必考但考法越来越阴这套笔试题里排序和查找是常客。但你别指望它问“快速排序的时间复杂度”这种送分题。它更常见的是问“在什么情况下快速排序的时间复杂度会退化为O(n²)”“归并排序的额外空间复杂度是多少”“二分查找的循环终止条件要怎么判断”。我在这里说几个高频“坑”快速排序在最坏情况每次选的基准都是最大或最小元素下退化为O(n²)但平均是O(n log n)。这个“最坏情况”非常容易被忽略。归并排序的额外空间是O(n)不是O(1)。很多人记住了“稳定”却忘了“空间”。二分查找的边界处理重点看while (left right)还是while (left right)这决定了你缩边界时到底是mid还是mid 1/mid - 1。我建议你固定一种写法刷题时反复用形成肌肉记忆考试时就不容易乱。如果你只想复习一种排序以外的算法我推荐top K问题。它几乎是大厂笔试的“钉子户”解法从“全局排序O(n log n)”到“堆O(n log k)”到“快速选择O(n)”层层递进。尤其在选择题里它特别喜欢问“用堆做top K时间复杂度是多少”答案是O(n log k)不是O(n log n)。原因很简单堆的大小一直是k每次插入/删除都是O(log k)总共有n个元素所以是O(n log k)。3.2 二叉树递归是基础非递归才是分水岭二叉树相关题目在这套笔试题里占比不低。前序/中序/后序遍历是地基但笔试真正爱考的是根据中序前序重建二叉树、层序遍历、求二叉树深度、判断一棵树是不是二叉搜索树。先说重建二叉树这个题。它的核心思路靠的是“前序找根中序分左右”。因为前序遍历的第一个节点一定是根节点然后在中序遍历里找到这个根节点它左边就是左子树右边就是右子树。递归处理左右区间就能复原整棵树。具体实现上不要傻乎乎地每次去substring拷贝数组那样空间复杂度和时间复杂度都很差。正确做法是传下标区间在原数组上操作。再说“判断二叉搜索树”。最常见的错误解法是“递归判断左节点值 根节点值 右节点值”但这是一个典型陷阱。二叉搜索树要求的是“整个左子树的所有节点都小于根节点整个右子树的所有节点都大于根节点”而不是只看直接子节点。正确做法有两种一种是中序遍历看结果是否严格递增另一种是递归时传递一个取值范围区间(min, max)。3.3 动态规划该背模板的还是得背说实话这套题里的动态规划题目难度不算高一般是“爬楼梯”“最长公共子序列”的变种。但它的价值在于帮你把动态规划的思考步骤固化下来。我做动态规划题目的标准流程是四步定义状态dp[i]表示什么找状态转移方程确定初始化和边界确定遍历顺序。很多人在“爬楼梯”这种入门题上不丢分但遇到“最小路径和”“编辑距离”就会乱根源就是没有形成“先定义状态再推方程”的习惯。你只要坚持这个流程哪怕题目没见过也能写出合理的递推式。这里分享一个小技巧如果实在推不出转移方程试着把问题缩小一维比如“从左上角到(i, j)的最小路径和”只关心最后一步怎么走——是从上边来还是从左边来。这一想方程往往就出来了。4. 操作系统与网络基础不牢地动山摇4.1 进程、线程与协程概念辨析要“滚瓜烂熟”这套笔试题在操作系统板块的考察重点很清晰进程状态转换、线程与进程的区别、死锁的必要条件、虚拟内存和页面置换算法。进程状态转换是一个高频选择题考点。你要记住五种状态新建、就绪、运行、阻塞、终止。注意“就绪→运行”是调度器分配的CPU“运行→阻塞”是等待某个事件比如I/O而“运行→就绪”是时间片用完被抢占。选择题特别喜欢把“运行→就绪”写成“等待I/O完成”那就是错的。死锁这块四个必要条件互斥、占有并等待、不可剥夺、循环等待必须倒背如流。选择题常考的是“破坏哪个条件可以防止死锁”。比如资源一次性分配就是破坏“占有并等待”可剥夺资源就是破坏“不可剥夺”条件。虚拟内存这块页面置换算法里LRU是重点。你要清楚LRU是“最近最久未使用”它和LFU最不经常使用完全是两回事。选择题如果混着考你要能一眼分辨。以及FIFO可能会出现Belady异常分配的物理块增多反而缺页率上升而LRU不会。4.2 TCP三次握手与四次挥手表情包都会背但细节会卡网络部分TCP的连接管理几乎是必考。三次握手、四次挥手的过程、状态变化、为什么要这样设计这些都是选择题和简答题的常见出题点。常见坑点第二次挥手时ACK和FIN是分开的因为被动关闭方可能还有数据要发所以先回ACK再把FIN和最后的ACK合并或稍后发送。选择题里经常把“四次挥手”简化成“三次挥手”问你对不对答案通常是“不对除非正好数据发完了”。TIME_WAIT状态是谁进入的主动关闭方。为什么要有TIME_WAIT为了保证最后一个ACK能到达对方以及让旧连接的报文段自然消失。持续时间是2MSL。这个点特别爱考务必记牢。第三次握手可以携带数据而第一次和第二次不行。这是RFC里的细节选择题偶尔抠这个。4.3 HTTP与缓存虽然基础但选择题从来不做慈善这套笔试题也会考到HTTP基础知识。比如状态码的含义404是Not Found500是服务器内部错误301是永久重定向302是临时重定向。很多人混淆301和302其实区别就一句话“书签会不会被更新”。关于缓存Cache-Control和Expires的区别也常考。Expires是HTTP/1.0的是一个绝对时间Cache-Control: max-age是HTTP/1.1的是一个相对时间。选择题问“哪个优先级更高”答案是Cache-Control。我在实际项目里吃过这个亏给静态资源设置Expires但没设Cache-Control结果某些代理服务器不认Expires导致资源反复回源。后来统一改成Cache-Control: max-age31536000并配合ETag问题才解决。这类题不是纸上谈兵真能救你的线上服务。5. 语言基础与设计模式C/Java的细节陷阱5.1 指针、引用、内存管理C考生的地狱难度这套题对C的考察相当细。const关键字的作用、指针和引用的区别、内存分配方式栈、堆、全局区、常量区、构造函数和析构函数的调用顺序、虚函数和虚表每一个都是选择题的“弹药库”。我印象最深的题目是考察“构造函数和析构函数的调用顺序”。答案是构造时先基类后派生类析构时先派生类后基类。但如果基类析构函数不是虚函数通过基类指针删除派生类对象时只会调用基类析构派生类的析构不会执行导致内存泄漏。这就是“虚析构函数”存在的意义。还有一道容易错的题sizeof一个空类是多少答案是1不是0。因为C标准规定同一个类型的对象不能拥有相同的地址所以编译器会给空类分配1字节的占位空间。但如果这个类里有虚函数那就要加上虚表指针的大小在64位平台上是8字节。这题真的每年都能见到。5.2 关键字与语法糖Java的必考清单Java部分这套题常考的有final、static、String的不可变性、和equals的区别、HashMap和Hashtable的区别、异常处理机制。HashMap和Hashtable的区别是一个经典考点Hashtable是线程安全的方法用synchronized修饰HashMap不是Hashtable不允许nullkey 和nullvalueHashMap允许一个nullkey 和多个nullvalue初始容量和扩容策略也不同。另一个高频考点是String为什么不可变。核心原因是为了安全性和效率字符串常量池缓存、哈希缓存、以及作为参数传值时不会被意外修改。选择题如果问“StringBuilder和String的区别”你要能答出前者可变、效率高但线程不安全StringBuffer则在线程安全方面做了同步。5.3 设计模式不考代码考“场景识别”这套笔试题里的设计模式题往往不是让你手写单例而是给你一个场景问“以下哪种设计模式最适合”。这类题的关键是理解每个模式的使用场景。我总结了一个快速判断表设计模式核心场景识别关键词单例全局唯一实例配置文件、连接池、日志对象工厂创建对象逻辑复杂或需要解耦根据不同参数创建不同对象观察者一对多依赖状态变化通知所有依赖者事件监听、消息订阅策略算法可替换多种支付方式、多种排序算法装饰器动态增强对象功能避免子类爆炸IO流、加缓存、加日志选择题里如果出现“日志记录器全局只有一个”直接选单例如果出现“价格计算有多种策略运行时切换”直接选策略。不要犹豫。6. 数据库与SQL看起来简单最容易丢分6.1 索引原理为什么你的SQL慢数据库这块这套笔试题喜欢考索引相关的概念。你要清楚B树索引为什么适合数据库树矮、磁盘IO次数少、叶子节点有链表便于范围查询。而哈希索引适合等值查询不适合范围查询。选择题里有个常见陷阱给一个联合索引(a, b, c)问哪些查询能用上索引。答案是a、a,b、a,b,c都能用但b、c、b,c用不上因为最左前缀原则。除非你给b单独建索引否则查询条件里没有a时联合索引就废了。另一个高频考点是“回表”这个概念。如果查询的列不在索引里InnoDB 需要通过主键再查一次聚簇索引这个过程叫回表。覆盖索引就是“查询的列全部在索引里”不需要回表。这个机制理解了很多“为什么这个查询慢”的题就迎刃而解。6.2 事务隔离级别与锁事务的ACID特性、隔离级别、脏读/不可重复读/幻读的区别也是必考。四个隔离级别从低到高是读未提交、读已提交、可重复读、串行化。MySQL 默认是可重复读而Oracle默认是读已提交。这个很容易考到。“脏读”是读到未提交的数据“不可重复读”是同一查询在同一个事务里读到不同的行因为别的事务提交了更新“幻读”是同一查询读到新增的行。注意可重复读解决了不可重复读但在标准SQL下仍可能出现幻读。InnoDB 通过间隙锁gap lock在可重复读级别下解决了幻读问题但这是MySQL的实现特例选择题里要小心区分“标准SQL行为”和“InnoDB具体实现”。6.3 SQL写法别把简单的题写复杂这套笔试题的SQL题一般不会太难主要是分组统计、连接查询、子查询。但越是简单越容易在细节上翻车。比如“查询每个部门薪资最高的员工”很多人的第一反应是写GROUP BY department_id然后MAX(salary)。这个思路有个问题你只能拿到部门和最高薪资拿不到对应的员工姓名。正确做法是先用子查询找到每个部门的最高薪资再通过JOIN把员工信息带出来SELECT e.* FROM employee e JOIN ( SELECT department_id, MAX(salary) AS max_salary FROM employee GROUP BY department_id ) t ON e.department_id t.department_id AND e.salary t.max_salary;这个题的教训是SQL里“分组后取组内其他字段”不是一个简单的GROUP BY能搞定的需要自连接或窗口函数。考试如果允许用窗口函数ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC)更简洁。7. 编程题详解从题意到AC的完整思考链条7.1 真题风格还原链表类题目这套题里的编程题常见风格是“给定一个链表进行某种操作”。为了让这个过程更具体我们拿一个高频原型题来演示判断链表是否有环并返回环的入口节点。第一步先画图。这个习惯我在前面提到过不是废话。画图之后你会发现判断是否有环用快慢指针快指针每次走两步慢指针每次走一步。如果快指针走到了空节点说明无环如果快慢指针相遇说明有环。第二步找环的入口。这需要一点数学推导。假设链表起点到环入口的距离是a环入口到相遇点的距离是b环的周长是c。慢指针走了a b快指针走了a b n*c因为快指针速度是慢指针的2倍所以2(a b) a b n*c即a b n*c。这意味着从相遇点再走a步会回到环入口。所以算法是快慢指针相遇后把快指针重置到起点然后两个指针都每次走一步再次相遇的位置就是环入口。代码实现struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; ListNode* detectCycle(ListNode *head) { ListNode *slow head, *fast head; while (fast fast-next) { slow slow-next; fast fast-next-next; if (slow fast) { fast head; while (slow ! fast) { slow slow-next; fast fast-next; } return slow; } } return nullptr; }这个题在笔试里至少有两个隐藏分点一是边界条件空链表、单节点无环二是能否在O(1)额外空间内完成。第二种解法如果用了哈希表虽然能做对但空间复杂度是O(n)在强调复杂度的评分标准下会扣分。7.2 真题风格还原动态规划类题目再拿一道典型的动态规划题来讲最长递增子序列LIS。这个题出现过很多变体在笔试题里属于中等难度。动态规划解法很直观定义dp[i]表示以nums[i]结尾的最长递增子序列长度。状态转移方程dp[i] max(dp[j] 1) for all j i and nums[j] nums[i]初始化每个位置的dp[i] 1因为单个元素本身就是长度为1的递增子序列。最终答案是max(dp)。代码实现int lengthOfLIS(vectorint nums) { int n nums.size(); if (n 0) return 0; vectorint dp(n, 1); int ans 1; for (int i 1; i n; i) { for (int j 0; j i; j) { if (nums[j] nums[i]) { dp[i] max(dp[i], dp[j] 1); } } ans max(ans, dp[i]); } return ans; }这个解法的时间复杂度是O(n²)面试时通常会被追问“能不能优化到O(n log n)”。优化思路是用一个辅助数组维护“当前长度下的最小末尾值”然后二分查找。但笔试阶段能写出O(n²)的DP已经算基本盘O(n log n)是加分项。我建议你在刷题时把每个题目都按“暴力法 → 优化法 → 最优法”三个层次过一遍。笔试时先写最稳妥的解法保证AC如果你还有余力再在注释里补充优化思路。别一上来就挑战最优解编码时间有限容错率低。7.3 笔试编程的“提分习惯”编程题不只是“写对”而已。根据我的实战经验下面这几个习惯能帮你稳稳地多拿分第一统一变量命名风格。不要一会儿i一会儿index一会儿p一会儿nodePtr。在笔试的在线编辑器里变量名写得太随意很容易把自己绕晕。我习惯用slow/fast、left/right、cur/prev这类语义明确的名字。第二先写主框架再填细节。拿到题先写函数签名、边界判断、主逻辑骨架然后再逐行补齐。这样即使后面时间不够评卷人也能看到你“思路是对的”不至于拿零分。第三提交前跑一遍“手写测试用例”。在脑子里模拟空输入、单元素、全相同元素、逆序输入、大规模数据。这5个用例跑完大多数bug都能暴露出来。8. 备考策略与避坑指南少走弯路8.1 时间分配不要“平均用力”这套笔试题覆盖的范围很广如果你只剩两周时间备考我建议你按下面的权重分配精力模块优先级理由算法与数据结构极高题量大编程题分值高操作系统高概念固定性价比高计算机网络高TCP、HTTP高频数据库中高索引、事务必考语言基础中高看你投递的岗位语言要求设计模式低题量少掌握6-8个常用即可不要因为“设计模式很有趣”就一直刷设计模式的题那是本末倒置。优先把算法题刷到“看到题目就能反应出解法类型”的程度再去看其他模块。8.2 刷题方法一题多解的价值我在刷这套题的时候习惯把每一道选择题都往“如果是简答题我该怎么解释”的方向去准备。这个习惯帮我省了很多后续复习时间。举个例子选择题问“为什么TCP要三次握手”如果你只记住了“确保双方收发能力正常”那还不够。你要能展开说第一次握手客户端确认自己发送正常服务端接收正常第二次握手客户端确认自己收发正常服务端确认自己收发正常第三次握手服务端确认客户端接收正常。 也就是说三次握手让双方都确认了“自己的发送能力”和“对方的接收能力”。类似地看到排序算法不要只记复杂度表要能说出“为什么归并排序是稳定的快排不是”这种底层原因。真正吃透一道题比稀里糊涂刷十道题有用得多。8.3 常见失分点我见过的那些“可惜”结合我自己的做题经验和我帮人改卷时看到的情况以下几个失分点最常见粗心型失分题目问“以下哪个是错误的”你选成了“以下哪个是正确的”。这种情况在选择题里简直防不胜防。我建议你把题目里的“错误”“正确”“不属于”“属于”这些词圈出来让自己强制注意。边界型失分编程题没处理空指针、空数组、负数、溢出等情况。尤其是二分查找的mid (left right) / 2如果两个数很大可能溢出建议写成mid left (right - left) / 2。概念型失分把“死锁的四个必要条件”和“避免死锁的方法”搞混。这是两回事前者是“必须同时满足才会死锁”后者是“通过破坏条件来避免”。表达型失分简答题只写结论没有推导过程。比如问你“为什么哈希表查找是O(1)”你只写“因为哈希函数”那大概率只能得一半分。你要写清楚“哈希函数计算槽位理想情况下无冲突所以一次定位时间复杂度O(1)最坏情况下所有元素在同一个槽位退化为O(n)所以实际中需要负载因子控制和再哈希”。9. 最后这套题应该如何“用好”把一套老题刷完、看懂、记住答案其实只发挥了它三成的价值。真正有用的是用这套题做“基线测试”找出自己的知识短板再针对性地补强。我个人的做法是第一遍严格计时模拟真实笔试环境做完整套题统计每个模块的得分率。然后针对得分率最低的两个模块用一周时间专门复习。一周后再把错题做一遍看是否真正掌握了。如果还有错就说明不是“忘了”而是“理解有偏差”需要找资料重新学而不是继续刷题。这套题我再拿出来看其实已经不再是“面试题”了而是一份“计算机基础知识点清单”。它提醒我无论技术栈怎么变算法、操作系统、网络这些底层知识永远是研发工程师的基本盘。把这些基础打牢无论是跳槽面试、还是日常写代码排查问题都会顺很多。如果你正在准备笔试我的建议是别贪多先把这套题吃透。把每一道题涉及的知识点延展开形成自己的笔记。这份笔记的价值会远超题目本身。