免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

腾讯研发笔试题精讲:C/C++、数据结构与网络考点全解析

腾讯研发笔试题精讲:C/C++、数据结构与网络考点全解析 腾讯2016研发工程师笔试题三是我校招季刷得最仔细的一套题。那会儿我把能找到的历年大厂笔试题全翻了一遍多数卷子做一遍就扔了但唯独这套题我反复重做了三轮还把每道题背后的知识点单独拉了一张表。不是因为它难到离谱而是它考得非常“准”C/C、数据结构、操作系统、网络、数据库、Linux研发岗笔试会碰到的方向几乎全覆盖而且很多题表面考概念实际考的是对底层机制的理解。对正在准备校招或者想跳槽大厂研发岗的人来说这套题是一个很好的自检工具。这篇文章我按自己做题时的顺序把卷子拆成几个部分讲清楚每一类题在问什么、解题时应该怎么想、哪里容易丢分最后再聊聊怎么把一套题的价值榨干。后面不会再逐字复述原题但涉及的考点和推导过程都会按当年考题的风格完整还原。1. 试卷整体长相题量、题型与时间分配1.1 这套题的基本盘先说卷子本身。这套题网上流传的还原版大体由三部分构成不定项选择题约40道、填空题5道左右、编程题2到3道考试时长90分钟。不定项选择的可怕之处在于多选、少选、错选都可能不得分所以它不光是考你会不会还考你判断得准不准。从科目分布看C/C和数据结构加一起能占到一半以上剩下的分散在操作系统、网络、数据库和Linux命令。下面是一个大体占比具体年份可能略有浮动但大方向不会变科目方向大致题量占比高频考点C/C 与内存25%虚函数、构造函数/析构函数、指针与引用、内存对齐、const/static数据结构与算法30%链表、二叉树、排序、查找、动态规划、复杂度分析操作系统15%进程线程、死锁、内存管理、页面置换计算机网络12%TCP/UDP、三次握手、拥塞控制、HTTP数据库10%索引、事务隔离级别、SQL语句Linux/其他8%常用命令、文件权限、系统排查为什么选择题叫“不定项”而不叫“单选”因为很多题开着“下列哪些说法正确”看上去每个选项都长得差不多。这里有个很实际的建议先把有绝对把握的选项选上拿不准的不要硬选。腾讯这类大厂的笔试是机器判分不同年份规则不一样有的多选倒扣有的少选给部分分但无论如何先保证确定的分数落袋为安。时间分配上选择控制在55分钟内填空15分钟剩下至少20分钟给编程题最后留一点时间检查。1.2 拿到卷子后我建议的做题顺序我的习惯是先花2分钟把整张卷子扫一遍不看具体题目只看题号和分值。编程题放在第一顺位做因为编程题需要的是一个清醒的大脑如果先做一个小时选择题大脑已经被各种细节塞满再写代码容易在边界条件上翻车。做完编程题再回头做选择心态会稳很多。选择题内部也有顺序先做数据结构、C/C这些自己最有把握的模块把分数稳稳拿到操作系统、网络、数据库穿插在中间遇到完全没思路的题先标记跳过不要在单题上卡超过2分钟。笔试不是每道题都要做对而是要在有限时间里拿最多的分。填空一般考概念默写或简单计算比如给一段代码问输出结果这部分往往是拉分关键因为很多人会忽略。你不需要写得像高考作文但关键结果必须准确。这里也有个经验编程题如果实在没思路先把暴力解法写出来。笔试的判题通常有部分分空着是0分暴力解至少能过一部分测试点。不要一上来就空想最优解先把能跑的代码提交再逐步优化。2. C/C与内存管理这套题的“基础分”来源2.1 高频考点虚函数、指针、const、static先说虚函数。这套题几乎每年都会在虚函数上做文章最经典的问题有三个虚函数表在内存里的位置、含有虚函数的类的大小、构造和析构函数能不能是虚函数。构造函数不能是虚函数原因是对象还没构造出来虚表指针还没初始化虚函数表根本无处可寻析构函数一般推荐声明为虚函数尤其是基类指针指向派生类对象时不然 delete 时只会调用基类析构函数派生类资源就泄漏了。类的大小这个点也容易记混假设一个类只有一个 int 成员大小为4一旦加入一个虚函数就会多出一个虚表指针64位系统下类大小变成164字节 int 8字节指针再按8字节对齐。可以简单写个类验证class A { public: int x; virtual ~A() {} };这类题考的不是你能不能背出大小而是你知不知道“虚函数会让类多一个虚表指针”。指针与引用这块我见过不少人把“引用是别名指针是地址”背得滚瓜烂熟一到代码题就懵。比如问引用能不能重新绑定不能。引用有没有空引用没有。指针可以指向空引用必须初始化。const char* p 和 char* const p 的区别前者是 p 指向的内容不能通过 p 修改后者是 p 本身不可再指向其他地址。这类题没有捷径只能靠多写。static 在不同场景下含义完全不同修饰全局变量时限制作用域修饰局部变量时延长生命周期修饰成员函数时让它不依赖具体对象。笔试很喜欢把这三个混在一起出形式通常是“下列说法正确的是”一个选项一个坑。2.2 内存布局与常见陷阱内存布局题是这套题的经典送命点。先把五个区列出来栈、堆、全局/静态存储区、常量存储区、代码区。栈由编译器自动分配释放堆由程序员 malloc/new 分配、free/delete 释放全局变量和 static 变量放在全局区字符串常量放在常量区代码区放指令。有一类题目给出一段代码问你哪个变量在哪个区这类题只要把区划清楚就能拿分。另一个高频考点是内存对齐。结构体的大小不是简单把所有成员大小加起来而是按成员中最大对齐数对齐。举个例子struct { char a; int b; char c; } 在32位系统下大小是12不是6因为 int 要 4 字节对齐a 后面会填充 3 字节c 后面再填充 3 字节。很多人栽在这个地方不是不懂而是忘了算末尾填充。内存泄漏和野指针也是必考。new 出来的对象一定要 delete但 delete 之后指针最好置空否则就是野指针。也别以为用智能指针就万事大吉循环引用会让 shared_ptr 无法释放。笔试里的选项经常是“delete p 后 p 指向的内存被释放p 变成空指针”——这句话是错的delete 释放内存但不会把指针本身置空p 成了悬垂指针。这种细节题没有实际写过几遍很难一眼看出错在哪。我的建议是准备一个小 demo把这些场景都写一遍打印地址验证比死记结论管用得多。2.3 我的失分点拷贝构造与赋值运算符我最想单独拿出来说的是拷贝构造和赋值运算符。这两者很容易混但笔试经常考。拷贝构造是创建一个新对象时调用写法是 A a2(a1); 或 A a2 a1; 赋值运算符是针对已有对象写法是 a2 a1;。区别是有没有“新对象”出现。默认拷贝构造做的是浅拷贝当类里有指针成员时两个对象会指向同一块内存析构时 double free。正确的做法是写深拷贝或改用智能指针。有一道题我当时做错了题目给了两个类的定义问调用了几次构造函数、几次拷贝构造、几次析构函数。很多答案是靠返回值优化RVO减少了一次拷贝如果不知道编译器优化就会多算一次。这类题目与其背答案不如在本地跑一下用编译器输出验证印象会深很多。还有几个很容易一起考的析构函数设置为 private 会让对象无法在栈上创建只能在堆上 new把构造函数声明为 explicit 可以避免隐式类型转换。这些点单独看都简单但放到同一个不定项选择题里就成了区分度很高的题。3. 数据结构与算法从选择题到编程题的完整链路3.1 链表与二叉树的选择题套路数据结构部分链表和二叉树是绝对主角。链表题最常见的是判断有没有环、找环入口、找中间节点、反转链表。判断环用快慢指针快指针每次走两步慢指针每次走一步有环就一定会相遇找环入口在相遇后让一个指针回到头节点两个指针都改为每步走一个节点再次相遇的位置就是环入口。这套推导过程最好能手写出来因为选择题不给你运行机会只能靠推演。链表的题目还要注意边界比如空链表、只有一个节点、循环链表很多选项就靠这种边界条件区分对错。二叉树的选择题更偏遍历、深度、完全二叉树性质。给你一个前序和中序遍历结果让你求后序遍历这是最基础的题必须会。另一个常见的是计算二叉树的深度、第 k 层节点数、叶子节点数利用递归公式深度 max(左子树深度, 右子树深度) 1。完全二叉树的性质也常考有 n 个节点的完全二叉树深度是 floor(log2 n) 1如果按层序编号节点 i 的左孩子是 2i右孩子是 2i1从1开始编号。这些都是可以快速心算的考场上很划算。除了这些二叉搜索树的中序遍历结果是有序序列这个性质也经常被用来设计题目比如判断一棵树是不是 BST只需要看其中序遍历是否严格递增。3.2 排序与查找复杂度不是背出来的排序在这套题里不会让你手写快排但会考不同排序在不同场景下的选择。比如数据基本有序时用什么排序插入排序接近 O(n)。数据量很大且要稳定排序内存装不下用什么归并排序因为它适合外部排序。快排最坏情况下 O(n^2)但它平均性能最好还常用于求 TopK 的 partition。堆排序最坏也是 O(n log n)但不稳定。这里容易丢分的是把稳定性记混稳定排序包括冒泡、插入、归并、基数不稳定包括选择、快排、堆排、希尔。记忆方法很简单选择排序会跨位置交换快排、堆排都有跳跃交换所以天然不稳定。别问我为什么拿个小数组手推一遍就懂了。二分查找更是考边界条件的大户。模板是 while (left right)mid left (right - left) / 2防止 leftright 溢出。等号该不该取缩边界时是 mid 还是 mid±1不同写法结果完全不一样。我见过最坑的选项是“在有序数组中查找某个数二分查找一定比顺序查找快”——错数据规模很小或数组里有大量重复时顺序查找可能更快而且顺序查找对缓存友好。复杂度分析是另一个重点递归算法用主定理比如 T(n)2T(n/2)O(n) 是 O(n log n)。这些概念理解了选择题很快就能排除两个选项。3.3 一道典型编程题的完整推导Top K 问题编程题一般不会只考一个孤立知识点而是综合考察数据结构和算法设计。我拿 TopK 问题举例因为它在这类试卷里出现频率非常高从一个长度为 n 的数组中找出最大的 K 个数。最简单的解法是排序时间复杂度 O(n log n)但这不是面试官想看到的。第二种做法是维护一个大小为 K 的小顶堆遍历数组时如果当前元素比堆顶大就弹出堆顶、插入当前元素时间复杂度 O(n log K)。第三种做法是借助快排的 partition 思想每次把数组分成两部分如果枢纽位置大于 K在左半部分继续找否则在右半部分继续平均 O(n)。笔试编程题如果时间充裕我建议写堆或 partition 解法如果时间紧张先写排序解法拿部分分。实际写代码时边界条件要特别注意。K 为 0 怎么办K 大于 n 怎么办数组为空怎么办这些判断在选择题里可能是一个选项在编程题里就是测试用例过不过的问题。还有一个隐藏考点如果 n 特别大内存放不下整个数组要如何处理这时候快排的 partition 没法用了只能靠堆或者分布式处理。这个扩展思路经常作为面试官追问点出现在后续面试中。面试时被问“这个方案还能不能优化”答出“用堆可以处理海量数据”基本就能过关。4. 操作系统与网络看似分散实际都是高频4.1 进程与线程考的不是定义是并发结果操作系统部分选择题最爱考进程和线程。传统的定义题很少更多是给你一段多线程并发代码问输出结果或是否会发生死锁。要答对这种题得清楚进程是资源分配的基本单位线程是 CPU 调度的基本单位同一个进程内的线程共享地址空间、文件描述符但不共享栈和寄存器。共享变量如果没有加锁i 这种操作并不是原子的两个线程同时执行可能出现结果比预期小的情况。这套题里有个常见陷阱问“多个线程访问同一个全局变量肯定会出错”——这句话是错的如果所有线程都只读不会出错即使写如果做了同步也不会出错。它考的是你能不能把“共享”和“并发安全”分开。死锁的四个必要条件互斥、持有并等待、不可剥夺、循环等待。选择题经常问“打破哪个条件可以预防死锁”比如一次性申请所有资源是打破持有并等待资源可抢占是打破不可剥夺破坏循环等待可以给资源编号、按序申请。还有银行家算法这类安全性检测题比较费时间但只要按表格一步步推正确率很高。我的经验是把进程的已分配、还需、可用三个矩阵画出来逐个检查哪个进程能完成能完成的先执行然后释放资源循环下去。如果存在一种顺序让所有进程完成系统就是安全的。4.2 TCP/IP三次握手和拥塞控制网络题在研发岗笔试里占的比重不如 C但每年都会出现。三次握手是必考客户端发送 SYN服务端回复 SYNACK客户端再发送 ACK。选择题问“为什么需要第三次握手”标准答案是防止已失效的连接请求突然传到服务端导致资源浪费。如果不理解可以想象客户端发送的第一个 SYN 在网络里滞留了很久客户端已经超时重发并完成连接结束后那个旧 SYN 才到达服务端如果没有第三次握手服务端会以为是新连接白白分配资源。这个例子只要记住选择题就不会错。TIME_WAIT 也是一个高频考点。主动关闭连接的一方会进入 TIME_WAIT 状态等待 2MSL最大报文段生存时间后才真正关闭目的是保证最后一个 ACK 能被对方收到同时让旧连接的报文在网络中消失。笔试经常问大量 TIME_WAIT 出现在服务端还是客户端通常出现在主动关闭连接的那一端。某年真题里给的场景是服务端上 TIME_WAIT 连接很多问可能原因是什么其实是服务端主动关闭了连接可能是超时而不是客户端。这种题需要把整个连接断开流程完整走一遍才能选对。拥塞控制和流量控制也常考。流量控制是接收方通过窗口大小限制发送方的发送速率属于端到端的控制拥塞控制是网络发生拥塞时发送方主动降低速率四个算法慢开始、拥塞避免、快重传、快恢复。选择题喜欢问慢开始阶段拥塞窗口怎么增长每收到一个 ACK拥塞窗口增加一个 MSS实际是指数增长到达 ssthresh 后进入拥塞避免线性增长。如果连这都不清楚多半是把慢开始和慢启动搞混了。4.3 Linux与内存管理Linux 命令在笔试里经常以“以下哪个命令可以查看端口占用”的形式出现。netstat、ss、lsof 都可以但 ps 不行。查看进程 CPU 和内存占用用 top查看系统内存用 free查看磁盘用 df。这类题没有技术含量纯粹靠平时积累。2016 年那会儿 Docker 还不像现在这么普及但 Linux 基本命令已经考得很频繁了因为研发工程师日常就要和服务器打交道。备考时每天花 10 分钟过一遍常用命令性价比很高。内存管理方面页面置换算法是另一个考点最佳置换 OPT、先进先出 FIFO、最近最久未使用 LRU、时钟 Clock。选择题会给一串访问序列让你算缺页次数。FIFO 有 Belady 异常增加页框反而缺页更多LRU 是基于局部性原理的近似最优算法。做这类题一定要按表格慢慢推把每个时刻装入的页面都列出来不要跳到中间。我当年就是懒得画表靠心算结果算错一道后来老老实实画表正确率立刻上去了。5. 数据库与Linux研发工程师的“隐藏科目”5.1 索引与事务数据库两座大山数据库题在研发岗笔试里占比不大但每次出现都很要命。索引这块B 树是高频词。为什么 InnoDB 用 B 树而不是 B 树或者红黑树因为 B 树的叶子节点用链表串联范围查询只需要遍历链表不用中序遍历整棵树而且 B 树所有数据都放在叶子节点内部节点可以存更多索引树高更矮磁盘 IO 次数更少。选择题还会问聚簇索引和非聚簇索引的区别聚簇索引的叶子节点直接存整行数据一张表只能有一个非聚簇索引存主键值查询时需要回表。这个“回表”概念很重要很多题围绕它出。事务的 ACID 四个特性原子性、一致性、隔离性、持久性。隔离级别从低到高读未提交、读已提交、可重复读、串行化。每个级别能解决什么问题要记清楚读未提交可能脏读读已提交避免脏读但不可重复读可重复读避免不可重复读但 MySQL 默认的 RR 级别还通过间隙锁消除了幻读串行化最安全但性能最差。笔试经常给一个场景问“哪个隔离级别下不会发生 XX 问题”本质就是考这几个级别的层次。还有一个容易混的点事务日志——redo log 保证持久性undo log 保证回滚和 MVCCbinlog 用于主从复制。这些概念一列出来选择答案基本就出来了。5.2 常见SQL题不是刷题是刷手感SQL 题一般不会太复杂最多考多表联查、分组统计和去重。比如“查询每个部门工资最高的员工”用窗口函数 ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC)或者用关联子查询。2016 年的笔试很多还不支持复杂窗口函数但数据库原理里会考。SQL 手写题我建议先写 SELECT...FROM...WHERE...GROUP BY...HAVING...ORDER BY... 的完整顺序理清逻辑再动笔。常见错误是 WHERE 和 HAVING 的混用WHERE 在分组前过滤行HAVING 在分组后过滤组聚合条件必须放 HAVING。这个点几乎年年考。Linux 题和 SQL 题有个共同特点考得浅但考得宽。像是文件权限 chmod 755 表示 owner 可读可写可执行、group 和 others 可读可执行数字对应 rwx421。查看进程树用 pstree查找文件用 find 和 grep 组合find / -name *.log 2/dev/null | xargs grep ERROR。这些命令不需要全部背下来但常见的一定要会用。我的做法是把高频命令整理成一篇笔记考前只看笔记。不要试图在笔试时现场 man在线笔试系统一般没有这个命令而且时间也来不及。5.3 一道综合场景题数据库Linux的串联应用真正让一部分人翻车的是那种把 Linux 和数据库串起来考的场景题。题目不一定给你完整 SQL而是说“日志文件每一行包含时间、用户ID、接口名、响应耗时要求统计每个用户调用次数最多的前3个接口”。你需要先用 awk 按字段拆分再用 sort 和 uniq -c 统计计数最后用 head 取前3或者把数据导入 MySQL再用 GROUP BY 和 ORDER BY。考的不是哪个命令有多难而是你在有限时间内能不能搭建一个完整的处理链路。我的建议是至少会一套 shell 解法awk {print $2} user_api.log | sort | uniq -c | sort -rn | head -n 10这套命令写熟遇到类似问法能很快作答。如果笔试允许你选方言你也可以直接用 Python 脚本但脚本出错的风险更高shell 管道更稳妥。6. 从这套题反推的备考策略与教训6.1 哪些知识点可以战略性放弃刷完这套题你会发现它的覆盖范围很广但深度并不算变态。有些知识点可以考虑战略性放弃。比如复杂的红黑树旋转、KMP 算法的 next 数组推导、B 树删除节点这些在研发岗笔试里出现的概率低投入产出比不高。不是说完全不用看而是不要在它们上面花太多时间。优先保证的是排序和二叉树的代码实现、链表操作、C 虚函数和内存布局、进程线程与死锁、TCP 三次握手、SQL 基本查询。这些是确定性考点先把这些练到闭着眼都能写出来再谈扩展。时间有限的时候懂得取舍比盲目刷题更重要。另外智力题和数学题也要适当准备。这套题偶尔会有一两道类似“烧绳子计时”或者“概率题”它考的是思维而不是知识储备。遇到这种题别慌先把能列出的条件写出来用最笨的方法推导如果 2 分钟没有头绪果断放弃因为后面还有编程题等着你。我见过有同学在一道智力题上耗了 15 分钟结果编程题没写完这是最大的失误。6.2 错题复盘方法建立自己的考点地图刷题最重要的不是做了多少套而是把错题转化成自己的知识漏洞清单。我当时的做法是每套题做完先不看解析自己对着答案把每道错题的知识点标出来比如“虚函数表”“TCP 状态转移”“B 树范围查找”然后在一张 Excel 表里分类记录。每做一套就统计一次哪个知识点出错最多。两周之后表格能清楚告诉你该补哪一块。这比漫无目的地刷题高效得多。腾讯这套题三特别适合用来做这个训练因为它覆盖面广一次能暴露很多薄弱点。把同类错题放在一起对比你会发现很多题都是同一个底层概念换了件马甲。还有一个细节错题不要只看正确答案要看错误选项为什么错。很多选择题是“三对一错”或“一对三错”光知道正确选项没有意义。你必须能解释每一个选项对在哪、错在哪这才是真正掌握了。我习惯在错题旁边用红笔写上“选项 A 错误原因是...”如果写不出来说明还没吃透回头翻书也要把它弄明白。6.3 最后两周怎么用这套题如果你离笔试还有两周我建议这样安排第一周按科目过一遍基础知识点每天做题不超过 30 道第二周用整套题
返回列表