腾讯研发工程师笔试题解析:从链表到TCP的经典考点与刷题策略 📅 发布时间:2026/8/30 8:05:34 👁 浏览次数: 前几天整理网盘翻出一份存了很久的“腾讯2016研发工程师在线模拟笔试题”。那会儿为了备战秋招这套题我前后刷了三遍每次都能刷出点新问题来。搁现在看题目风格确实和如今的大厂机考不太一样但里面真正经典的考点——链表操作、进程调度、TCP握手、哈希冲突——依然是面试官翻来覆去喜欢问的东西。这篇文章不打算把整套题贴出来凑数而是想从“这套模拟题到底想考你什么”出发把里面的核心技术点、解题思路、做题时的踩坑记录以及在校招场景下应该怎么高效刷题一次性说透。说的是2016年的题但底层的知识体系和答题逻辑放到今天依然管用对准备研发岗笔试的人有一定参考价值。1. 拿到这套题先搞清楚它到底在考什么1.1 整体结构科目分布和出题风格先给大家还原一下这套题的大致结构。2016年腾讯的在线模拟笔试题以选择题为主外加少量编程题和简答题。选择题覆盖范围非常广大致包括这几个模块数据结构与算法约30%-35%涉及数组、链表、树、图、排序、查找、动态规划操作系统约15%-20%涉及进程线程、内存管理、死锁、文件系统计算机网络约15%-20%涉及TCP/IP协议栈、HTTP、DNS、网络层与传输层C/C语言基础约20%涉及指针、内存布局、sizeof计算、虚函数、STL底层原理数据库与Linux约10%涉及SQL语法、索引原理、常用Shell命令这个配比放在今天看依然可以看作大厂研发岗的技术笔试“标准营养餐”。2016年的特点在于整体偏重基础理论编程题不要求完整跑通代码而是考察思路和关键代码片段和现在牛客网、力扣上动辄整道大题的风格存在差异。当年的模拟题更想摸清你“计算机科班基本功扎不扎实”而今天的机考更偏向“工程实现能力”。1.2 题目难度分层基础题、进阶题和“劝退题”我把这套模拟题里的题目按难度做了个粗分类方便大家建立预期难度层级典型考点题目占比目标人群基础层sizeof结果判断、排序算法时间复杂度、数组与指针区别40%所有投递者进阶层二叉树遍历变体、哈希冲突处理、TCP三次握手状态40%有系统复习的候选人劝退层红黑树插入调整、动态规划状态转移、Linux内核调度细节20%真正拉开分差的深度题说实话“劝退层”的题目放在今天考核应届生很多人会直接懵掉。但反过来想正是这些题筛出了那些“平时没事就翻《算法导论》和《深入理解计算机系统》的人”。所以刷这套题重心不能只放在“把题做对”还要看懂出题人为什么这样出。2. 核心技术点逐一拆解光背答案没用得明白原理2.1 数据结构与算法万变不离其宗的几道经典题先说数据结构。这套题里有一类特别典型的问题给你一段代码问输出是什么。比如有一道题是考数组下标和指针偏移的int a[5] {1, 2, 3, 4, 5}; int *p a 2; printf(%d %d %d, *p, *(p1), p[1]);输出结果是3 4 4。很多人第一眼觉得*(p1)和p[1]应该相等没错它俩就是等价的。但这里真正想考的是你清不清楚a 2实际上做了指针的整数加法移动的是2 * sizeof(int)个字节而不是单纯在地址上加了2。这背后是C语言指针运算的基本规则指针加n移动的是n个元素的距离。这类题现在看起来基础但越是基础的东西越容易在关键时候掉链子。还有一类高频考点是链表的操作。模拟题里给了一道“判断链表中是否存在环”的题目要求分析快慢指针解法的时间复杂度。快指针每次走2步慢指针每次走1步如果有环两者必然在某个节点相遇如果没有环快指针会先到达链表末尾。时间复杂度是O(n)空间复杂度是O(1)。这个结论很多人能背出来但题目进一步问“为什么快指针走2步一定能追上而不是刚好跳过慢指针”——这就不是背结论能答出来的了。关键点在于当慢指针进入环之后快指针已经在环内了。每次移动快指针相对慢指针靠近1步。哪怕快指针一开始在慢指针后面距离为环长减1每轮追近1步最多走完环长减1轮就追上了。所以只要快指针移动速度比慢指针快1步即可不是非2步不可。树这一块模拟题中出现了二叉树的前序、中序、后序遍历结果推导给了前序和中序要求还原后序。这是数据结构课上的经典题目但笔试里的陷阱在于构建二叉树时递归边界条件特别容易写错。我当时就吃过亏——递归函数里把mids - 1和mids 1搞反导致栈溢出。后来总结出一个小技巧重建二叉树时先画出递归树确定左右子树的区间范围再动手写代码能避免80%的下标错误。2.2 操作系统进程线程、死锁和内存管理操作系统模块的考点比较集中。进程和线程的区别已经被问烂了但2016年这套模拟题换了个姿势给了一段代码让你判断子进程和父进程在fork之后各自的执行顺序和变量值。int val 1; if (fork() 0) { val 2; printf(child val%d\n, val); } else { val 3; printf(parent val%d\n, val); }这里考的核心是子进程在fork之后获得父进程地址空间的一份拷贝父子进程的变量在拷贝后互不影响。所以子进程把val改成2父进程的val依然是1再改成3。输出要么是child val2先打印然后parent val3要么反过来取决于调度顺序。这个考点到今天依然是常客因为很多分布式系统、多进程架构都依赖对fork语义的准确理解。死锁产生的四个必要条件互斥、持有并等待、不可剥夺、循环等待也是必考项。模拟题出了一道“破坏循环等待条件的方法有哪些”的题目答案包括给资源编号并按序申请。这里容易混淆的是破坏“持有并等待”的方法是“一次性申请所有资源”破坏“不可剥夺”的方法是“当进程无法获得新资源时释放已有资源”。四个条件对应四种破坏策略一一对应不能张冠李戴。内存管理部分虚拟内存和页面置换算法是重点。LRU最近最久未使用算法在模拟题里给了一个长度为4的页面访问序列要求计算缺页次数。这类题的解法是画一个时间线表格每次访问页面时更新栈中内容的顺序。实际操作里LRU的硬件实现成本很高所以很多系统用近似LRU的Clock算法这属于后续延伸的知识点但面试时能主动说出来是加分项。2.3 计算机网络TCP/UDP、HTTP状态码和DNS解析网络部分的题目坦率讲是很多非科班同学的丢分重灾区。这套模拟题有一道高频题TCP三次握手过程中第二次握手失败会怎样选项包括“客户端进入TIME_WAIT状态”“服务器端重传SYNACK”“连接直接建立失败”。正确答案是服务器端会在超时后重传SYNACK重传次数由操作系统的TCP_SYNACK_RETRIES参数控制。这个知识点结合了Linux网络内核配置和TCP状态机属于那种“学的时候觉得没用排障的时候才知道有多重要”的内容。HTTP状态码也是必考模块。模拟题出了一道“服务器响应状态码404、500、302含义分别是什么”的题目本身简单但选项里加入了“403 Forbidden”和“301 Moved Permanently”作干扰。我当时也犯了糊涂403是服务器理解请求但拒绝执行404是找不到资源500是服务器内部错误302是临时重定向。关键容易混淆的是301和302301是永久重定向302是临时重定向。如果做SEO的人看这个差别非常致命——301会把权重传递到新地址302则不会。DNS解析过程在2016年的模拟题里以“访问www.qq.com时DNS的解析顺序”出现。正确顺序是浏览器缓存、操作系统缓存hosts文件、本地DNS服务器、根域名服务器、顶级域名服务器、权威域名服务器。这道题的干扰项是把根域名服务器和权威域名服务器的顺序颠倒。很多人不理解为什么需要根域名服务器——因为根域名服务器告诉你“.com”这个顶级域名的服务器在哪里相当于给你一个“下一步该问谁”的指路牌。2.4 C/C语言基础指针、内存布局和虚函数表C/C的题2016年腾讯模拟考里占比不低。指针和数组的辨析前面已经提过一个例子这里说另一个易错点二维数组和指针数组的区别。int a[3][4]; int *p[3]; int (*q)[4];p是一个指针数组包含3个指向int的指针q是一个指向含4个int元素数组的指针。很多人把p和q搞混甚至在int *p[3]和int (*q)[4]之间迷失。理解方式很简单[]的优先级高于*所以int *p[3]先被解释为一个数组数组元素是int *类型而int (*q)[4]加上括号后q先被解释为一个指针指向一个int[4]的数组。优先级规则是这类题的钥匙强行背类型定义没有意义。sizeof相关的题也是常客。模拟题里有一道struct Node { char a; int b; char c; }; sizeof(struct Node) 的值是多少答案是12在32位或64位Linux默认对齐规则下。因为 char 占1字节int 占4字节内存对齐时b需要从偏移地址4开始所以a后面会有3字节填充整个结构体大小由最大对齐数4对齐到12字节。很多刷题的人死记“结构体大小是最大成员大小的整数倍”却没有理解填充字节的意义——对齐是为了让CPU访问数据时不需要跨越两次内存读取属于空间换时间的经典做法。虚函数表在模拟题中出现了一道比较深的题有类A包含虚函数类B继承类A且自身也有虚函数问对象的内存布局。答案是对象内存最前面是一个指向虚函数表的指针vptr虚函数表里先存放A的虚函数地址再存放B的虚函数地址。覆盖override的虚函数会在B的虚函数表中替换成B的实现。这个概念延伸到工程上是理解多态和RTTI的基础也是我后来排查线上C服务段错误时的核心知识储备。3. 实操复盘按时间线走一遍完整的刷题流程3.1 准备阶段模拟题的正确打开方式很多人拿到一份模拟题就开始闷头做做完对答案错了再背一遍答案然后换下一份。这样刷3套题下来碰到原题能答对稍作变形就懵。我后来调整了刷题方式效果好了很多分享给大家参考。第一遍限时闭卷做。需要模拟真实笔试的紧张感闭卷、不查资料、不看手机给自己掐表。2016年这套模拟题的选择题部分大约60分钟完成如果超时就停下标记没做完的题目。这一遍的目的是“暴露问题”而不是“做对题目”所以分数难看也没什么好灰心的。第二遍逐题复盘。重点不是看错题答案是什么而是问自己三个问题出题人想考哪个知识点这道题对应的知识点在我的知识体系里处于什么位置如果我把这个知识点以另一种形式呈现能不能做对复盘时把所有错题的知识点整理到一张表里我当时整理出来的高频错点包括指针运算优先级、TCP状态迁移、LRU缺页计算、死锁预防策略、二叉树重建边界条件。第三遍重做错题。隔一周左右把错题重新做一遍。如果这次能全对说明知识点真正内化了如果还错说明当初只是“记住答案”而不是“理解原理”需要回到教材或网课里把相关知识重新过一遍。3.2 选择题的时间分配60分钟怎么花选择题部分想拿高分时间分配不能太随意。我的策略是“三轮答题法”第一轮用时15分钟快速过一遍所有题把一眼就能看出答案的题直接选上。这类题通常是基础概念题比如“TCP三次握手的第二个报文是什么”。拿到这些保底分心态会稳很多。第二轮用时30分钟解决需要计算和推导的题比如sizeof计算、LRU缺页次数、二叉树遍历序列推导。这类题需要动笔不能光靠心算。我建议在草稿纸上把关键步骤写清楚避免思路断层。第三轮用时15分钟集中攻克不确定的难题或者重新检查之前拿不准的题。注意除非非常确定自己第一次选错了否则不要轻易改答案——尤其是那些凭第一直觉选的题。人的第一直觉往往基于潜意识里的知识积累改答案大概率是越改越错。这套方法不仅仅是针对2016年这套模拟题后面我参加机考时也一直在用。时间压力下策略比硬啃更重要。3.3 一道动态规划题的完整解答过程模拟题里有一道动态规划题让我印象很深难度适中且很有代表性一个机器人位于m×n网格的左上角每次只能向下或向右移动一步要走到右下角有多少种不同的走法。我拿到这道题时的第一反应是直接套组合数公式总共需要走(m-1) (n-1)步其中向下m-1步向右n-1步所以答案是C(mn-2, m-1)。这确实是正确答案而且时间复杂度O(1)。但是笔试环境里经常要求在代码框里实现一个函数所以更通用的解法是动态规划int uniquePaths(int m, int n) { vectorvectorint dp(m, vectorint(n, 0)); for (int i 0; i m; i) dp[i][0] 1; for (int j 0; j n; j) dp[0][j] 1; for (int i 1; i m; i) { for (int j 1; j n; j) { dp[i][j] dp[i-1][j] dp[i][j-1]; } } return dp[m-1][n-1]; }状态转移方程是dp[i][j] dp[i-1][j] dp[i][j-1]含义很简单到达当前位置的路径数等于从上方过来的路径数加上从左方过来的路径数。边界条件是第一行和第一列都只有1条路径因为只能一直向右或一直向下。在16年那种“写关键代码片段”的判卷风格下只需要把上面这个转移方程写清楚就能拿到大部分分数。现在的机考要求完整通过测试用例那就要进一步考虑优化——把二维dp压缩成一维数组空间复杂度从O(mn)降到O(n)。我在刷题时就把这几种解法都写了一遍遇到类似题就不会慌。3.4 代码题答题现场不追求一次跑通追求思路清晰在线笔试环境里写代码和本地IDE里写代码是两种体验。没有语法高亮、没有断点调试、编译报错信息也可能很隐晦。2016年这套模拟题的编程题虽然不难但现场写代码的限制条件让我印象很深刻。我的做法是先在草稿纸上把思路写成伪代码再转换成正式代码。比如说模拟题里的“合并两个有序链表”这道题伪代码就是新建一个dummy头节点 cur指针指向dummy while l1和l2都不为空 如果l1-val l2-valcur-next指向l1l1向后移动 否则cur-next指向l2l2向后移动 cur向后移动 循环结束后cur-next指向不为空的那个链表 返回dummy-next伪代码能帮你在不陷入语法细节的情况下把逻辑理清楚减少写出逻辑漏洞的可能。然后在代码框里逐行翻译最后检查一遍边界条件——两个链表都为空、其中一个链表为空、l1和l2长度相等。在线笔试时怎么强调边界检查都不过分很多看起来通过率很高的题实际考试里挂在边界case上的人不在少数。4. 这套题暴露出的知识盲区与典型错误盘点4.1 我看周围人做模拟题时最容易翻车的三类题刷这套题时我拉了三个同学一起做统计了一下错题分布发现有三类题的错误率最高有很强的共性。第一类是“关于数组和指针的sizeof计算”。这里有个巨大的认知误区认为数组名就是指针。实际上数组名在大多数表达式中会退化为指向首元素的指针但在sizeof运算中完全不同。sizeof(arr)返回整个数组占用的字节数sizeof(ptr)只返回指针本身的大小。一个特别经典的题目是char *str hello; char arr[] hello; sizeof(str) // 在64位系统上是8指针占8字节 sizeof(arr) // 6包括结尾的\0第一反应容易直接写“都是5”那就掉坑里了。第二类是“TCP四次挥手的TIME_WAIT状态存在原因”。正确理解是TIME_WAIT让主动关闭方等待2MSL最长报文段寿命确保最后一个ACK能被对方收到同时让本连接产生的所有报文从网络中消失避免影响新连接。很多人能背出状态迁移图但被问到“如果服务端处于TIME_WAIT状态会导致什么问题”时就答不上来了。实际工程中服务端大量TIME_WAIT连接会导致端口资源耗尽这需要调整内核参数或使用长连接来规避。第三类是“数据库索引的最左前缀原则”。模拟题给了一个联合索引(a, b, c)问哪些查询能用上索引。正确答案是where a1、where a1 and b2、where a1 and b2 and c3都能用上而where b2和where c3用不上。这个知识点太容易踩坑了很多人以为联合索引就相当于对每个字段都建了单列索引这是错的。联合索引建立的是“前缀索引”必须先有最左边字段的条件后续字段的索引才能被利用。4.2 这些错误背后的知识体系缺陷错题只是表象背后的知识体系缺陷才是真正需要修复的。我复盘后发现高频错误往往指向同一个根源知识点之间没有形成网络。以“sizeof加数组指针”为例很多人的错误不是不知道“数组名可以退化为指针”而是没有把“退化”和“上下文”绑定在一起。在sizeof和操作符中数组名不退化在其他表达式中退化为指针。如果能把“数组和指针的区别”做成一张逻辑图把sizeof、、函数形参传递、下标访问都串起来这个知识块就不容易出错。同理“TCP连接管理”的错误根源往往是孤立地记状态迁移图没有把“为什么需要这些状态”和“报文的可靠性传输要求”关联起来。把TCP和UDP对比着学把三次握手、四次挥手和“客户端/服务端各需要确认什么”连起来理解比死记状态名有用得多。所以刷这套模拟题的价值不只是查缺补漏更是倒逼你把自己的知识体系结构化。这个体会比任何一道题的正确答案都值钱。4.3 老题新做2016年的题放到现在有什么启发聊到这里可能有人会问2016年的题都过去这么多年了技术栈都换了几轮现在刷它还有意义吗我的看法是单看题目本身时效性确实在下降比如里面有些Linux命令、某些C标准库细节已经是老古董了。但笔试背后要考察的核心素质——基础功扎不扎实、分析问题有没有章法、代码能不能处理边界——从来没有变过。今天的面试官依然会问“链表有没有环”“TCP为什么要三次握手”“联合索引为什么最左匹配”只是换了个问法甚至直接让你手写代码。换一个角度说一套老题是很好用的“知识体系标尺”。你可以拿它来做一次自测如果在不复习的情况下这套题能拿到80%以上的分数那你的计算机基础基本是够用的如果低于60%就说明某些基础模块已经生疏了哪怕你已经工作了好几年也需要警惕——基础不牢后面看源码、做性能优化都会遇到瓶颈。5. 结语这套模拟题背后的复习方法论回到标题上“腾讯2016研发工程师在线模拟笔试题”表面上是一套题本质上是一次对计算机基础知识体系的完整体检。我至今保留着这套题的复盘笔记偶尔翻一翻还能看到当年在页边空白处写下的各种吐槽和灵感比如“数组指针又忘了画图”比如“快排最坏情况要是每次都选到最大元素栈会爆”。如果你正在准备大厂研发岗笔试我的建议是不必太纠结于题目的年份也不必迷信网上的“押题”和“题库”把一套经典模拟题吃透做到能清楚地讲出每道题背后的知识点再去做其他题会轻松很多。所谓“万变不离其宗”基础扎实了题目怎么换都能应对。最后分享一个我后来一直在用的刷题习惯每做完一份题用一张A5纸把错题对应的知识点、错误原因、正确思路写下来贴在手边的墙上。每周扫一眼一个月后你会发现那些曾经让你头疼的重难点不知什么时候已经变成了自己的舒适区。这套老题有没有过时不好说但这个习惯真的可以一直用下去。