从研发笔试题看视频平台技术岗:C++内存、TCP与高并发考点全拆解
1. 笔试到底在筛什么人:从PPTV的业务属性反推考点
2015年前后的视频行业,正处于移动端爆发、版权大战、带宽成本高企的混战期。PPTV作为老牌网络电视平台,研发岗位的笔试题目天然带着强烈的业务烙印:海量用户并发访问、视频文件的分发存储、播放器内核优化、广告系统的高可用,这些业务痛点会直接影响出题方向。
我当时拿到这套题的第一反应是:它和纯粹刷题型的互联网公司笔试不太一样。很多题目并不直接考“你会不会这个API”,而是在考“你有没有见过真实线上的问题”。比如同样考内存管理,它不会只问malloc和free的配对,而是会往缓冲区溢出、内存碎片的方向带;同样考网络,它不会只让背三次握手,而是会结合流媒体传输的特性来问。
这套笔试题的核心筛选逻辑可以拆成三条:
- 基础是否扎实。C/C++、操作系统、网络、数据结构这几块是硬底子,不扎实的人连海选都过不去。
- 有没有工程思维。纯理论正确还不够,题目里经常埋“边界条件”“异常处理”“资源释放”这些工程坑。
- 有没有业务 sense。视频网站的场景下,缓存怎么设计、并发怎么扛、播放卡顿怎么排查,这些都是送分题还是送命题,取决于你平时有没有思考过。
所以准备这套题,不能只看《程序员面试金典》,更要带着“我在为一家视频公司干活”的视角去梳理知识体系。下面我会把整套笔试题涉及的核心模块逐一拆开讲,每一块都会给出考察意图、典型题型、参考思路和踩坑提醒。
2. 核心考点拆解:每个模块背后的考察意图
2.1 C/C++与内存管理:不只是问指针,而是问你会不会“失控”
视频领域早期的C/C++代码量非常大,播放器内核、流媒体协议栈、转码服务,全是贴近底层的活。所以C/C++题目在笔试里占比很重,而且普遍偏向内存安全和指针运用。
典型的考法有几种:
- 指针和引用的区别。很多候选人能答出“引用是别名,指针是地址变量”,但问到“什么时候必须用指针不能引用”就卡住了。参考答案的方向是:需要重新指向、需要指向空值、需要做指针运算时用指针;而重载操作符、拷贝构造等场景必须用引用。
- const的各种组合。
const char *p、char *const p、const char *const p这三个的区别几乎是必考。我的建议是记口诀:const修饰的是它左边的内容,如果左边没有就往右看。const char *p左边是char,说明指向的字符不可变;char *const p左边是*,说明指针本身不可变。 - 内存泄漏和野指针。这题在笔试卷上经常以“找错”或“改错”的形式出现。常见的坑包括:忘记释放malloc的内存、释放后没有置NULL、返回了局部变量的地址、越界访问后导致堆损坏。我见过一个印象深刻的反例,有人用realloc之后没接收返回值,一旦realloc失败原本的指针就丢了,这就是典型的“内存失控”。
- 字节序问题。视频编解码涉及大量二进制数据,大端小端的问题常出现在联合体、强制类型转换的题目里。曾经有道题让你判断当前系统是大端还是小端,用union就能很巧妙地解决。
我平时带新人的时候常讲一句话:C/C++笔试题的高分选手,不是背了多少语法细节,而是能写出“出了异常也不会崩”的代码。答题时除了给出正确代码,最好主动写出防御性处理,比如参数检查、返回值判断、资源释放,这些细节非常加分。
2.2 数据结构与算法:重点在链表、二叉树和排序
数据结构与算法是笔试的大头,PPTV这套题里的算法题难度属于中等偏上,不搞竞赛级别的冷门算法,但也不会让你轻松AC。
先看链表。链表反转几乎是标配,但考法可以翻出很多花活:递归反转、迭代反转、K个一组反转、判断是否有环、找环的入口。我记得有一题是“判断两个链表是否相交,并找到第一个相交节点”。最直接的思路是哈希表,但如果题目要求O(1)空间,就要先分别遍历两个链表拿到长度差,然后让长的先走差值步,再同步走直到相遇。这题考察的是空间复杂度的敏感度,很多人能想出哈希法,却想不到双指针法。
二叉树同样是出题热点。层序遍历、前中后序遍历的递归与非递归写法、根据前序和中序重建二叉树、求二叉树深度、判断平衡二叉树,这些都属于基础题。有一道题印象很深:求二叉树中两个节点的最近公共祖先。如果树节点有parent指针,可以转化成“两个链表找交点”的问题;如果没有parent指针,递归的时候要处理好“左子树找到、右子树找到、两边都找到”三种情况。
排序算法这块,快排的复杂度推导、稳定性分析、适用场景是选择题和简答题的常客。需要记住:快排平均O(n log n),最坏O(n²),不稳定;归并稳定,空间O(n);堆排不稳定,但适合Top K。有一类很喜欢的出题方式是“给一个场景让你选排序算法”,比如“100万条记录按时间排序,内存足够,用什么排序”,答案往往是快排或归并,“如果内存只有10MB呢”,那就得考虑外部排序或堆排了。
算法大题方面,动态规划是高频方向。典型题目包括最长公共子序列、最长递增子序列、编辑距离、01背包。我不建议死记状态转移方程,而是先画dp表格理清递推关系,再动手写代码。有个容易被忽略的细节:很多DP题目里的“dp[i]”只依赖前一行数据,这时候可以用滚动数组把空间从O(n²)降到O(n),这种优化在笔试加分项里很有分量。
2.3 操作系统与并发:从原理到实战的鸿沟
操作系统部分的笔试题,重点几乎永远是三大块:进程与线程、死锁、内存管理。
进程与线程的经典问题是“区别与联系”。基础答案是进程是资源分配的基本单位,线程是CPU调度的基本单位,同一进程的线程共享地址空间,进程之间相互独立。但视频网站场景下,这道题会往“并发模型”的方向延伸。比如问“一个播放服务要支持大量用户同时观看,用多进程好还是多线程好,为什么”,参考答案要给出:多线程能共享播放状态、上下文切换成本低,但一个线程崩溃会影响整个进程;多进程隔离性强,但IPC成本高。线上常见方案是“多进程 + 多线程”混合,比如Nginx的worker进程模型,或者像Chromium那样一个tab一个进程。
死锁的四个必要条件(互斥、占有并等待、不可剥夺、循环等待)要能默写,更重要的是会答“如何避免”。比如破坏“占有并等待”可以通过一次性申请所有资源;破坏“不可剥夺”可以在申请不到新资源时释放已持有的资源;破坏“循环等待”则是对资源编号按序申请。笔试里经常给一段多线程代码,让你判断“这段代码会不会死锁”,这种题一定要先画出资源分配图,别凭感觉。
内存管理方面,虚拟内存、页表、缺页中断、LRU置换算法都是考点。有一年考过“LRU缓存怎么用数据结构实现”,这题其实是在变相考察你对哈希表和双向链表的组合运用。get和put都要O(1)复杂度,哈希负责快速定位,双向链表负责维护访问顺序,两者组合就能搞定。这道题放在“操作系统”部分,其实是借缓存来考察你对局部性原理的理解。
还有一个高频点:多线程交替打印或用信号量实现生产者消费者。代码本身不难,但要注意条件变量的“虚假唤醒”问题,while循环判断条件比if可靠得多。这种题考察的是你有没有真正写过并发代码,而不只是背过概念。
2.4 网络基础与视频传输场景:熟悉又陌生的TCP
网络部分的题目,TCP和HTTP是绝对主角,但PPTV的出题视角会刻意往流媒体方向带。
TCP三次握手和四次挥手是送分题,但细节才是拉分项。比如“为什么三次握手而不是两次”,答案是防止已失效的连接请求突然到达服务器导致资源浪费;“为什么TIME_WAIT要等2MSL”,答案是确保最后一个ACK能到达对端,同时让旧连接上的所有报文自然消失。我见过的笔试题里,有反复问握手过程中序列号变化的,有问SYN Flood攻击原理的,这些都需要你真正理解状态机,而不是只会画三条箭头。
HTTP方面,GET和POST的区别、Cookie和Session的区别、状态码的含义属于基础款。稍微进阶一点的是HTTP的keep-alive机制、管线化(HTTP Pipelining)的队头阻塞问题,以及HTTP/2的多路复用为什么能解决它。视频网站对HTTP Range请求支持很重要,拖拽播放、断点续传都依赖Range头,笔试如果问“如何实现视频拖拽播放”,正确答案就是客户端发起带Range的GET请求,服务器返回206 Partial Content。
对视频业务而言,还有一个不能忽略的考点:TCP的拥塞控制。慢启动、拥塞避免、快重传、快恢复,这四个算法要能画图说明窗口变化。为什么视频卡顿?高带宽时延积、丢包导致的窗口骤减,都可能是直接原因。谈到“为什么有时候TCP不适合视频传输”,可以提一下UDP和RTP在实时流媒体里的作用,以及基于UDP的自定义可靠传输协议。这种回答能体现出你对业务场景有理解。
DNS解析流程、HTTP缓存策略(Cache-Control、ETag、Last-Modified)、CDN调度原理,都是视频网站必须依赖的基础设施。你要能完整说出“输入一个URL到播放器起播”的全过程,从DNS解析到CDN节点选择,从HTTP请求到流媒体切片分发。这些知识既能在选择题里考,也能在简答题里考。
2.5 数据库与SQL:索引和事务是命门
数据库题目在通用研发岗里不会出太深,但索引原理、事务隔离级别、SQL优化是绝对的核心。
索引方面,首先要答清楚B+树为什么适合做数据库索引。和B树比,B+树的数据都在叶子节点,非叶子节点能存更多索引项,树更矮,磁盘IO更少;叶子节点之间有指针串联,范围查询、排序查询非常高效。哈希索引适合等值查询但不适合范围查询,这个对比也是常考的。
我印象中有一道题是“给定一条慢SQL,让你分析原因并优化”,大概长这样:
SELECT * FROM play_record WHERE user_id = 12345 ORDER BY create_time DESC LIMIT 20;这题考察的点是联合索引和最左前缀原则。如果你只在user_id上建了索引,那排序就用不上索引,需要filesort;如果只建了(create_time),那user_id的等值查询又用不上。最优方案是建(user_id, create_time)联合索引,这样等值条件定位到user_id=12345之后,create_time在索引里本来就是有序的,可以避免排序。这个例子在视频网站的播放记录查询里非常典型。
事务隔离级别这块,读未提交、读已提交、可重复读、串行化四个级别要能按顺序说出来,并且说清楚各自解决的并发问题。其中MVCC(多版本并发控制)在MySQL InnoDB里的实现原理,是能拉开差距的问题。你要能解释undo log如何实现快照读,以及当前读和快照读的区别。注意中奖的是,MySQL默认隔离级别是Repeatable Read,但Oracle默认是Read Committed,这种“数据库产品对比”选择题以前就考过。
SQL语法本身注意点:JOIN和子查询的区别与性能陷阱、GROUP BY和HAVING的执行顺序、LIMIT大偏移量的优化(延迟关联、书签查询)。一些经典错误包括在SELECT里用不在GROUP BY中的非聚合列,这种SQL在SQLite/MySQL的宽松模式下能过,写出来就是扣分项。
2.6 手写算法题实战:从读题到AC的完整思路流
算法大题是最能体现“在线编程能力”的环节。这里我挑两个当时的高频题,完整还原解题思路。
第一个是“最长回文子串”。暴力解法是枚举所有子串判断回文,O(n³),显然不是出题人的预期。动态规划可以把复杂度降到O(n²),但更优解是中心扩展法,枚举每个中心(含单字符中心和双字符中心)向两边扩展,复杂度O(n²),空间O(1)。如果再进阶,还有Manacher算法能做到O(n)。笔试现场建议直接写中心扩展法,代码短、不易出错。核心代码大约是:
def longestPalindrome(s): if not s: return "" start, end = 0, 0 for i in range(len(s)): len1 = expand(s, i, i) len2 = expand(s, i, i + 1) length = max(len1, len2) if length > end - start: start = i - (length - 1) // 2 end = i + length // 2 return s[start:end + 1]第二个是“两个有序数组合并,不去重”。这题看似简单,但很多人在“从后往前合并”这个点上栽跟头。如果你把结果放在第一个数组后面预留的空间里,从前往后覆盖会破坏未处理的元素,从后往前遍历则每次取两个数组当前最大的元素放到末尾,完美避免覆盖问题。这道题是归并排序的核心子过程,也是“合并两个有序链表”的同源变体。
手写题的答题策略我总结了一套:
- 先写注释说明思路,再写代码,不要上来就敲。
- 边界条件优先考虑空输入、单元素输入、全相同输入。
- 写完代码之后,手动推一个测试用例,检查有没有越界。
- 复杂度分析单独写一行,明确时间复杂度和空间复杂度。
- 如果能想到更优解但时间不够,在注释里提一句“还有一个O(n)的解法”,让考官看到你的思考深度。
3. 高分现场的实战经验:时间分配与得分技巧
3.1 笔试现场的时间分配策略
两个小时的试卷,选择题、填空题、简答题、编程题混合在一起,最忌讳的是在选择题上纠结太久。我的建议是:先用10分钟左右扫一遍全卷,标记出自己一眼会做的题、需要思考的题、完全没思路的题。
答题顺序上,我个人的习惯是:
- 先做会做的基础题,快速拿分,建立信心。
- 再做有思路但需要写代码的题,这类题分值大,要留足时间。
- 简答题放在编程题前后看情况处理,如果简答题需要长篇论述,我会控制篇幅,踩点给分就好,不用穷尽文采。
- 完全没思路的题最后集中处理,能写几步算几步,哪怕只会写个暴力解也要写上去,空着必零分,写了还能拿步骤分。
有一个小技巧:编程题即使代码写不完整,也要把关键思路、核心数据结构的定义写在答题区。很多笔试采用人工阅卷,步骤分是真实存在的。你写出的链表节点定义、递归函数的返回条件、算法的主要循环逻辑,都能拿到可观的分值。
3.2 常见低级失误清单
这部分是我这些年批改笔试、带新人复盘时总结出来的高频失误,每一条背后都是真实的丢分教训:
- 编程题没注意输入输出格式。有的题目要求多组输入,有的要求输出后换行,读题不仔细直接0分。
- 变量命名混乱。临时变量i、j、k到处用,逻辑稍微复杂一点,写着写着就把自己绕晕了。建议使用语义化命名,比如pHead、slow、fast、dp[i][j]。
- 忘记处理空指针和空数组。判空这种事,笔试里没做就是直接扣分,做了不会被夸但能保底。
- 栈和队列搞混。不同语言对栈和队列的接口命名不一样,C++里stack用push/pop,queue用push/pop,但Java里Stack用push/pop,Queue用offer/poll。其实算法本身就会,卡在API上太冤了。
- 复杂度分析瞎写。有些人写O(n²)的算法,时间复杂度分析却是O(1),这说明没有真正理解代码的执行过程,会被扣“分析能力”的分。
- 手写代码不检查就交。写完代码至少花1分钟推演一个测试用例,很多边界问题就是这个步骤能救回来。
3.3 简答题的踩分技巧:会答和答得好是两码事
简答题是笔试里技术含量最高、也最容易拉开差距的题型。同样的知识点,有人拿一半分,有人拿满分,差别就在回答的结构和细节。
我总结的简答题踩分模板是:先说结论,再给原理,最后配例子。比如“TCP和UDP的区别”,不要只罗列“TCP可靠、UDP不可靠”,而是先说“TCP是面向连接的可靠传输协议,UDP是无连接的数据报协议”,再展开三次握手、拥塞控制、有序性保证这些机制差异,最后说“视频直播场景适合用UDP因为实时性优先,文件下载场景适合用TCP因为完整性优先”。这样的答案层次感强,评卷人一眼能看到要点。
还有一个容易被忽略的点:简答题里如果能画出状态图、流程图、分层架构图,往往能拿到印象分。所以平时练习时,要习惯把知识整理成图式结构。比如TCP状态转移图、进程状态图、CDN请求流程,你能在30秒内徒手画出来吗?能的话,这题就稳了。
4. 从笔试题反推技术成长方向:这套题真正想教你的东西
4.1 以题为镜:暴露出的知识盲区怎么补
这套笔试题做完,本质上是对自己技术栈的一次体检。如果你发现C/C++内存管理丢分多,说明近些年写业务代码写惯了,底层细节生疏了。我的建议是别急着刷题,先回到基础书上把“指针和内存”这一章彻底读一遍,再用小工具(比如Valgrind)去检测自己写的代码有没有内存问题,把“理论缺失”转成“动手验证”。
如果算法题做不完,说明刷题量不够,而且对题型不够敏感。我不推荐无脑海量刷题,更推荐按“数组/链表/栈/队列/哈希/二叉树/堆/图/DP”分模块训练,每个模块先用20道经典题打底,再看高频题型的套路。这里有个反常识的经验:真正提高做题速度的不是“做了多少题”,而是“总结了多少种解法模板”。
按我的经验,动态规划题可以总结成模板:状态定义是什么、base case是什么、状态转移方程怎么写、遍历顺序是什么、能否滚动数组优化。链表题总结成模板:虚拟头节点、双指针(快慢指针)、递归写法。树的题总结成模板:前中后序的递归与非递归、层序遍历的队列写法。模板在手,笔试时等于开卷考试。
4.2 视频行业研发的加分项:比普通笔试多准备的东西
如果你是冲着PPTV这类视频公司去的,除了通用的计算机基础,还有几个方向特别加分:
第一,音视频编码基础。H.264的I/P/B帧概念、编码原理、码率控制、GOP(Group of Pictures)结构,这些知识不用精通,但至少要能和面试官聊起来。有些笔试题会直接问你GOP大小对播放性能的影响。GOP太大,打开播放器时首帧解码慢;GOP太小,压缩率下降,相同码率下画质更差。这个权衡能答出来,说明你对视频行业的基本盘有认知。
第二,播放器工作原理。视频播放的完整链路是:网络请求→缓冲→解封装→解码→渲染→音画同步。笔试里经常考的“首屏时间怎么优化”,其实就是优化这条链路上的每一个环节:DNS预解析、CDN就近调度、起播前小包探测、渐进式播放不等完整文件下载。这些内容非常实战化,普通刷题型选手大概率答不出来,而你能答得越具体,越容易脱颖而出。
第三,系统设计题里的“高并发”概念。视频网站有一个常见设计题:“设计一个短链接系统”或者“设计一个视频观看统计系统”。我印象比较深的是观看统计系统,核心就是要支持千万级日活的UV统计。实现方案可以用HyperLogLog做近似去重统计,用Redis做计数器缓冲,再用异步任务批量落库。你要能解释清楚实时性和精确性之间的取舍,以及被问到“服务挂了怎么办”时的降级方案。
我特别建议准备这类业务场景题时,画一张“请求从用户到服务器再到存储”的分层架构图,把每个层的职责标清楚,评卷人看到这种系统思维,大概率会多给分。
4.3 笔试之后:如何把试卷变成面试的弹药库
笔试不是终点,它是一轮信息量极大的“考试”。笔试里出现的题目、你拿不准的知识点、你凭着记忆模棱两可写出的答案,全是后续面试的复习重点。我当年做这套题之后做了三件事:
第一,把所有没把握的题重新做一遍,用搜索引擎和官方文档确认答案。笔试时模棱两可蒙对的,比做错的更需要警惕,因为这说明你没有真正掌握。
第二,把笔试题目按“已掌握、待巩固、完全不会”分类,制定一个一周补齐计划。我的原则是:完全不会的模块优先补,因为面试时大概率也会被问到;待巩固的模块用碎片时间反复默写,比如TCP状态转移图就适合在通勤时默画。
第三,整理一套自己版本的“高频考点速查卡”。不是抄书上的定义,而是用自己能理解的方式写出来。比如我对“为什么需要三次握手”的速查卡是:
- 第一次:客户端说“我要连你”
- 第二次:服务器说“好的,我收到了,我也可以收到你吗”
- 第三次:客户端说“我收到了你的确认,开始传数据吧” 用这种口语化的表达,面试时你的讲述才会自然流畅,而不是像背课文。
5. 一些我想单独拎出来说的心得
写到这里,有几个关于备考和研究笔试题的真实感受,不吐不快。
第一,不要迷信“原题”。网上的笔面试题,年份、批次不同,内容差异很大,即使拿到了所谓“原题”,考察的知识点也可能换了马甲。比如今天考链表反转,明天可能考回文链表;今天考LRU,明天可能考LFU。与其背题,不如把每个核心知识点背后的原理彻底吃透。原理懂了,题怎么变都不慌。
第二,手写代码的速度和准确度一定要练。我在实际批改笔试时经常看到,候选人思路完全正确,但代码写出来要么缺头文件、要么漏分号、要么数组越界。这不是能力问题,是熟练度不够。建议平时就用纸笔手写代码,不依赖IDE的自动补全,逼自己一次性写出能直接运行的代码。
第三,笔试里的“业务感”真的很重要。同一道题,面试官想看到的不只是“你会做”,更是“你在真实的业务里用过”。比如设计一个缓存系统,你说了一堆LRU、Redis,但如果你能结合视频播放场景,说出“热门视频的元数据放本地缓存、播放URL用CDN缓存、用户观看进度用Redis持久化”,这个答案的含金量会完全不一样。这需要平时多留意业务场景,多想想“为什么这样设计”,而不是只停留在写CRUD。
第四,心态层面也要稳。笔试过程中遇到不会的题太正常了,别慌,也别在心态上崩塌。我当年也有一道关于红黑树的题目完全没思路,我的策略是先跳过,把所有会做的题拿满,最后剩5分钟回头写了个“红黑树是自平衡二叉查找树,插入删除时会通过旋转和变色保持平衡”的叙述性答案。虽然没拿到这题满分,但保证了整体不崩。笔试卷面满分不现实,关键是你的总分能过线。
第五,笔试后的复盘价值远大于准备本身。我习惯在笔试结束后两小时内做一次快速复盘,趁记忆还新鲜,把没把握的题记录下来。这个时间点你回忆的细节最丰富,等到第二天就只剩下“那道题我不会”的模糊感觉了。这个习惯让我在后续的面试复习里省了大力气。
PPTV那年的研发工程师笔试题,放在今天看,难度不算夸张,但考得非常全面,覆盖了从语言基础到系统设计的完整链路。这套题给我最大的启发是:技术面试不是考察你记住了多少知识点,而是考察你在真实生产环境里解决问题的潜力。笔试题只是这种考察的起点罢了。
