9月中旬我参加了2024年秋招腾讯音乐技术岗的第二批笔试。从投简历到收到笔试通知中间差不多一周那阵子我基本把生活切成了“刷题补基础看面经”三块。现在笔试过去一段时间了趁着记忆还新鲜我把整场笔试的题型构成、考点分布、现场节奏以及踩过的坑完整复盘一遍。这篇文章不聊虚的全是能直接参考的东西尤其适合正在准备秋招、投递腾讯音乐或类似音乐/内容类互联网公司技术岗的同学。先说一下腾讯音乐它旗下有QQ音乐、酷狗、酷我这些产品技术岗笔试的底层逻辑和腾讯集团校招基本一致但会带一些音乐业务场景的特征。比如题目会包装成“歌曲播放队列”“歌单去重”“热门歌曲TopK”这类业务问题而不是干巴巴的算法题。所以准备的时候除了常规的数据结构和算法还得对“音乐推荐、播放、榜单”这些业务场景有基本了解。1. 笔试整体结构与备考节奏第二批的定位和真实考场体验1.1 从投递到笔试的时间线复盘先说下时间线2024年秋招的节奏相当紧凑。腾讯音乐的校招官网8月中下旬就开放了投递第一批笔试大概在9月初第二批紧接着在一周之后。我是8月底投的简历当时没有抱太大希望因为投递时间比较晚结果9月中旬还是收到了第二批笔试通知。这点想提醒大家不要因为投递晚就放弃批次之间是独立的第二批依然有很大机会。从投递到笔试大概有一周的准备时间。说实话一周突击对算法功底好的人够用但如果基础一般想靠这几天把动态规划和系统设计补上来不现实。我的建议是平时算法能力应该保持在LeetCode中等题随手能写的水平笔试前一周只做两件事刷高频题翻基础八股。1.2 笔试平台、题型分部与时间分配这次笔试是在线作答全程双机位监控。第一大题是选择题覆盖计算机基础、网络、数据库、语言特性等大概20到30道第二大部分是编程题3到4道总分占比很高。总时长120分钟时间非常紧。我的做题顺序是先快速过一遍选择题会做的直接选拿不准的先标记不恋战。然后直接跳到编程题先花两分钟把四道题都扫一遍判断题目难度和熟悉的题型从最有把握的开始做。我个人的策略是编程题至少保住两道AC剩下的一道拿部分分另一道如果完全没思路就写暴力解碰运气。选择题控制在40分钟内完成给编程题留足80分钟。这道时间分配后来验证是合理的。因为编程题不仅考察思路还要处理输入输出、边界条件、内存限制实际的写码时间往往比预估要长不少。最怕的就是前面选择题磨太久后面编程题时间不够那基本就和这个岗位无缘了。1.3 “第二批”真题与第一批的重合度“第二批”并不代表更简单或更难。从我观察到的信息来看不同批次的题目有部分重合也有一部分是题库里抽的新题。所以强烈建议在笔试前把网上能搜到的第一批笔试回忆题翻一遍尤其是编程题的题型大概率会在第二批里继续出现类似方向。比如第一批考了场景模拟类的链表题第二批就出现了类似基于“播放队列”的滑动窗口题。知识点方向是一致的只是业务包装换了。这一点我觉得是腾讯音乐笔试和其他纯技术公司笔试最大的不同——他们真的很喜欢把算法题挂在音乐场景下考。2. 基础题深度拆解计算机网络、操作系统、数据库与中间件高频考点2.1 计算机网络考了什么选择题里网络部分占了大概三分之一比例不低。TCP/IP协议栈是绝对重点TCP和UDP的区别、三次握手和四次挥手的过程、TIME_WAIT状态的作用都是老生常谈但必考的内容。有一道题我记得很清晰问的是“主动关闭连接的一方在第四次挥手后进入什么状态为什么需要等待2MSL”答案自然是TIME_WAIT核心原因是为了保证最后一个ACK能到达对方、以及让旧连接的延迟报文在网络中自然消失。HTTP相关也考了比如HTTP/1.1和HTTP/2的区别、HTTPS的TLS握手大致流程。这背后关联的是“移动端音乐App在弱网环境下如何优化请求”这类面试题虽然笔试只考了选择题但原理一定要吃透后面面试会继续追问。还有一道DNS相关的题问的是访问一个视频/音乐链接时从输入URL到播放的整个流程中DNS发生在哪一步。这类题不难但只要没有从“浏览器缓存→系统缓存→路由器缓存→递归查询”这条链路去记很容易选错。2.2 操作系统进程线程、死锁与内存管理操作系统的考点主要集中在进程与线程、死锁、内存管理。有一道题是考进程和线程的通信方式四个选项里涉及管道、消息队列、共享内存、信号量要区分哪些是进程间通信哪些也可以用于线程间通信。这题容易翻车的地方在于很多人只知道进程间通信的方式却不清楚线程间通信其实大多用的是共享内存加锁的方式。死锁的四个必要条件属于送分题互斥、持有并等待、不可剥夺、循环等待。但考试不会直接问这四个条件而是给一个具体场景让你判断是否可能死锁或者问“破坏哪个条件可以避免死锁”。比如“资源一次性全部分配”破坏的是持有并等待“允许抢占”破坏的是不可剥夺。这种考法才是真实出题风格。内存管理的虚拟地址和物理地址转换也考了页表、页面置换算法属于经典。LRU页面置换给了访问序列问缺页次数这个没什么技巧就是手动画表模拟关键是别数错。2.3 数据库索引、事务与SQL优化数据库部分主要考MySQL。索引那块是重头戏B树索引结构、聚簇索引和非聚簇索引的区别、最左前缀匹配原则每一条都可能出题。有一道题很典型给了几个查询条件问哪个查询能用到联合索引(user_id, song_id, play_time)考察的就是最左前缀匹配光背结论不行的得能判断字段顺序。事务的ACID特性和隔离级别也是高频考点尤其要理解四个隔离级别能解决什么问题读未提交有脏读读已提交解决了脏读但可能有不可重复读可重复读解决了不可重复读但可能有幻读串行化全解决但性能最差。MySQL默认用的是可重复读这点也常被拿来出选择题。还考了一道SQL慢查询的优化思路问的是“查询某歌手的热门歌曲并排序怎么优化”选项里有加索引、分表、缓存、上ES。这题没有唯一标准答案但最稳妥的思路是从索引和缓存两个层面回答如果让我写成简答题我会说先通过联合索引覆盖(singer_id, play_count)减少回表再用Redis缓存热点榜单降低DB压力。2.4 Redis与消息队列中间件基础不能丢中间件考了Redis的几个经典场景主要是缓存穿透、缓存击穿、缓存雪崩的区别和应对方案。穿透是指查询不存在的数据导致请求打到DB解决办法是布隆过滤器或缓存空值击穿是指热点key过期瞬间大量请求打到DB解决办法是互斥锁或逻辑过期雪崩是大量key同时过期解决办法是过期时间加随机值。这三者的区别是必背的笔试选择题和面试都会问。消息队列考得比较浅问的是Kafka在异步解耦场景下的基本模型包括Producer、Consumer、Topic、Partition。因为腾讯音乐的播放量很大日志和埋点数据很多消息队列在实际业务里是核心组件所以知道基本的消息可靠性和重复消费问题就够了。2.5 结合音乐业务的语言与场景设计题编程语言题目方面C和Java都有涉及。C考了虚函数、智能指针、内存泄漏Java考了JVM内存区域、垃圾回收算法、HashMap底层原理。腾讯系很多技术栈用的是C和Go所以C的底层细节考得偏多比如“vector扩容机制”“map底层为什么用红黑树”。我的感受是语言题考得不深但都是平时写代码会碰到的基础坑如果平时只刷题不写工程代码反而容易丢分。场景设计题是腾讯音乐笔试的亮点。有一道题的大意是日活数千万的音乐App需要展示一首歌的热度值热度由播放次数、收藏数、分享数等加权计算并且要支持实时更新问你怎么设计存储和计算方案。这题不是纯考算法而是考架构思维。我当时选择了四个层面来答数据采集通过MQ异步上报实时计算用Flink做窗口聚合存储层用Redis保存实时热度历史数据落到ClickHouse做离线分析。虽然面试官不在现场但这类题目能反映你有没有分布式系统的基本认知。3. 编程题完整复盘合并区间、滑动窗口最大值与热门歌曲TopK3.1 编程题整体难度与题型分布这次笔试的编程题一共四道整体难度分布有梯度第一题属于签到题最后一题比较难。题型涉及数组区间处理、滑动窗口、堆排序/快速选择、字符串模拟。和音乐业务结合得很紧比如区间题包装成“合并播放时间段”滑动窗口题包装成“统计一段时间内热度最高的歌曲”TopK题直接就是“热门歌曲榜单”。如果你刷过LeetCode的hot 100拿到这些题的第一感觉应该是换皮而已。考点还是那几类双指针、哈希表、滑动窗口、堆、动态规划。所以这里想强调一个核心观点不要被业务包装吓到先去掉壳子看本质提取出题目真正的数据结构和算法模型。3.2 真题一合并重叠播放时间段区间合并这道题是经典区间合并题LeetCode 56原题换了个场景。题目大意是给定若干条App使用记录每条记录包含开始时间和结束时间需要把重叠的时间段合并返回合并后的总播放时长或合并区间列表。区间合并属于高频且容易拿分的题核心点就两个排序贪心。把区间按开始时间升序排序遍历时维护当前合并后的右边界如果当前区间的左边界大于上一个区间的右边界说明没有重叠就把上一个区间加入结果否则更新右边界为两者取最大值。这个思路时间复杂度是排序的 O(n log n)非常稳定。我贴一下当时写的代码用的C#include bits/stdc.h using namespace std; vectorvectorint mergeIntervals(vectorvectorint intervals) { vectorvectorint res; if (intervals.empty()) return res; sort(intervals.begin(), intervals.end()); int left intervals[0][0], right intervals[0][1]; for (int i 1; i intervals.size(); i) { if (intervals[i][0] right) { right max(right, intervals[i][1]); } else { res.push_back({left, right}); left intervals[i][0]; right intervals[i][1]; } } res.push_back({left, right}); return res; }这道题有几个容易出错的细节我现场就差点踩坑。第一输入数据可能不是按开始时间排好序的必须先排序第二边界处理上区间[1,3]和[3,5]按题目要求算重叠因为“同一秒”算连续播放所以判断条件必须是而不是第三最后一个区间在循环结束后要单独加入结果这个脑子一乱就容易漏。代码里我用了vector而不是pair纯粹是输出格式要求的便利提交的时候要注意题目要求返回的是二维数组还是打印结果。在线笔试有时候已经写好了函数签名只要求你填充实现这时候直接改函数体就行不需要自己写main函数。但也有要求完整可运行代码的两种模式我这次都遇到了提前把两种模式都练一遍很有必要。3.3 真题二滑动窗口内最大播放热度单调双端队列这道题比合并区间多绕了一步。题目业务包装是给定一个整数数组表示连续N个时刻每首歌的播放热度值再给定滑动窗口大小k要求输出每个窗口中的最大热度值。这就是LeetCode 239“滑动窗口最大值”的原题难度是Hard但其实是纸老虎。关于这道题最重要的结论是不要用暴力法因为数据范围很大每次在窗口里扫一遍找最大值时间复杂度是 O(n*k)线上数据直接超时。正确做法是维护一个单调递减的双端队列。单调队列的思路我给第一次接触的朋友解释一下。想象你有一个双端队列里面存的是数组下标。我保证队列里的下标对应的热度值是从大到小排列的队头就是当前窗口的最大值。每当新元素进入窗口时把队尾所有比它小的元素全部弹出因为它们既没有当前元素大又比当前元素更早离开窗口留着没用了。然后把当前下标推入队尾再检查队头下标是否已经不在窗口范围内如果是就弹出。#include bits/stdc.h using namespace std; vectorint maxSlidingWindow(vectorint nums, int k) { dequeint dq; vectorint res; for (int i 0; i nums.size(); i) { while (!dq.empty() nums[dq.back()] nums[i]) { dq.pop_back(); } dq.push_back(i); if (dq.front() i - k) { dq.pop_front(); } if (i k - 1) { res.push_back(nums[dq.front()]); } } return res; }这道题现场我卡了一小会儿原因是我在做“弹出队尾所有比当前元素小的元素”这步时用的是而不是。区别在哪里如果两个数相等用小于号会把前面的相同元素留在队列里虽然结果值一样但队列里的下标可能已经是过期的后续弹出的判断容易出错。建议直接用保证新元素进场时旧元素被清掉。单调队列的精髓在于“每个元素最多进队出队一次”所以整体时间复杂度是 O(n)。这比暴力的 O(n*k) 好了整整一个量级。笔试里数据量大到只能靠线性复杂度过的题非常多所以这个模板建议背下来最好是结合理解去写而不是死记硬背。3.4 真题三热门歌曲TopK堆与快选第三道编程题同样不陌生业务场景是一份歌单里有大量歌曲每首歌有播放次数需要找出播放次数最高的TopK首歌输出。常规考法就是LeetCode 347“前K个高频元素”或者更简单的“数组找第K大的数”。我的解法是先用哈希表统计每首歌的播放次数然后维护一个大小为K的小顶堆。小顶堆的堆顶是堆内最小的元素遍历全部歌曲时只要发现当前歌曲的播放次数比堆顶大就弹出堆顶、把当前元素入堆这样遍历完堆里剩下的就是最大的K个。用C的priority_queue默认是大顶堆需要自定义比较器或存入负数来实现小顶堆。#include bits/stdc.h using namespace std; struct Cmp { bool operator()(const pairstring, int a, const pairstring, int b) { return a.second b.second; // 小顶堆second小的在堆顶 } }; vectorstring topKElements(unordered_mapstring, int freq, int k) { priority_queuepairstring, int, vectorpairstring, int, Cmp pq; for (auto kv : freq) { if (pq.size() k) { pq.push(kv); } else if (kv.second pq.top().second) { pq.pop(); pq.push(kv); } } vectorstring res; while (!pq.empty()) { res.push_back(pq.top().first); pq.pop(); } reverse(res.begin(), res.end()); return res; }现场有个细节坑题目要求最终按播放次数从高到低输出我一开始直接把堆里的元素while取出来结果顺序是反的。堆顶是K个里最小的所以最后要reverse一下。这个点很小但很容易被忽略笔试的判题可不管你的输出顺序差多远一不过就全错。还有一种解法是快速选择时间复杂度平均 O(n) 比堆 O(n log k) 更快但对C选手来说实现起来容易在“partition边界”上出错笔试压力下不建议冒险。堆解法代码简单、不容易写错在时间有限、能AC就是王道的情况下堆是更稳妥的选择。3.5 需要留意的一道动态规划题第四道编程题我印象里是一道动态规划包裹的场景是“用户连续签到听歌每天可以选择听或不听要求不能连续缺席M天问N天内最多能积累多少积分”。这种题本质上就是带限制的线性DP状态设计起来比较直接。我的思考和推导过程是这样的定义dp[i][j]表示前i天结束且已经连续缺席j天时的最大积分其中j只能取0到M-1因为一旦达到M天就违规了。如果第i天选择听歌那dp[i][0] max(dp[i-1][所有j]) 听歌积分如果选择缺席那dp[i][j] dp[i-1][j-1]只从连续缺席j-1天转移过来。这个状态转移画出来后实现并不复杂唯一的难点是要处理“不能连续缺席M天”这个约束以及初始化时j0的情况。这道题我没能在规定时间内完全AC只跑通了对拍样例和部分用例。复盘时意识到我对“积分可能为负”这种情况没处理好初始值应该设为负无穷而不是0。这是很多DP题容易忽略的细节最大值问题里如果积分可以是负数0就不能当初始值。这个坑写在这里提醒后来的同学注意。4. 现场实操与避坑指南从环境准备到时间管理的完整心得4.1 笔试环境与设备准备这些细节不提前做考场会很难受在线笔试最怕的不是题难而是设备出问题。我这次是双机位主摄像头对着正脸副摄像头从侧后方拍桌面和屏幕。提前一天一定要做三件事第一检查电脑摄像头和麦克风能不能正常开启浏览器是否允许页面调用摄像头权限第二确保网络稳定最好插网线或离路由器近一点第三把手机拿来扫码监控的话要先想好手机怎么架临时找书堆很容易角度不对被判定为违规。我身边有同学在笔试时因为副机位角度问题被系统弹窗警告虽然最后没被取消成绩但心态直接崩了后面几道题完全没做出来。这种因为非技术因素丢分的情况真的很亏。建议提前一小时进入笔试等待页面把所有设备调试完然后静静刷两道简单题热热身再开始正式考试。4.2 输入输出与边界条件的坑代码写得对不代表能AC在线笔试的判题和本地自测差别很大。本地IDE跑一下样例通过了不代表线上能AC最大的坑往往在输入输出和边界条件上。这次考试我们提前知道编程题可能有多组输入所以处理循环读入是基本操作。有些题用空格分隔一行数据有些用逗号分隔逗号分隔的题尤其容易出错因为要先把字符串按逗号切开再转成整数。边界条件方面三组最容易翻车的情况是空数组、数组只有一个元素、数据范围超过int上限。第一道合并区间题如果输入是空数组直接返回空结果不处理就会越界崩溃。滑动窗口题如果k nums.size()代码要提前返回空数组否则整个过程都会出错。TopK题如果k 歌曲总数按题目要求应该返回全部歌曲而不是报错。这些场景平时刷题都要写在代码里宁可多写几行防御性判断也不要因为一个角落的用例挂掉。还有一个性能相关的坑C选手注意endl会强制刷新输出缓冲区比\n慢很多在输出量大的时候可能导致超时。循环输出结果的时候建议用一个字符串拼接所有答案最后一次cout输出或者至少用\n替代endl。4.3 做题节奏与心态管理保分优先别和难题较劲笔试的节奏直接决定成败。我的建议是编程题部分先快速浏览全部题目给每道题按“会做→有点难→没思路”分个级然后从会做的开始写。遇到卡住超过15分钟的题立刻停止跳到下一题。很多人在笔试中最大的问题是“不甘心”觉得一道题已经写了大半再想想就能出来结果死磕了40分钟后面三道题全没时间碰。这个思路是错的笔试是分数制先保证能做对的题全部AC再回头啃难题。我这次也差点踩坑。滑动窗口最大值那道题代码写完后本地跑测试用例通过了但线上提交却报错。我第一反应是算法问题往前排查了五分钟后来才发现是输入数组转换时的字符串格式问题其中带了一层嵌套括号没处理好。这种问题如果死磕代码逻辑就会越陷越深我当时的处理方式是先放下这道题去做后面TopK那道必拿分的题回来以后再重新读输入处理部分果然一下就定位了。心态上还有一点就是接受自己“不是所有题都能做出来”这个事实。就算目标是SP笔试拿满分也不是必须的关键是把你会的题做对。哪怕有一道题完全空白只要其他题质量高照样能进面试。怕的是会的题因为粗心没AC简单题最后输出顺序不对这些本不该丢的分一旦丢了才是最影响结果的。5. 笔试后的复盘与下一阶段衔接决定你走多远的往往在考场之外5.1 趁热打铁笔试结束后48小时内的复盘法很多人笔试结束之后就把题目抛到脑后这是很可惜的。人的记忆曲线决定了考试后48小时内是复盘黄金期过了这个时间连题目长什么样都会模糊。我建议一考完就打开文档把还记得的题目按“题目描述、数据范围、当时的思路、卡住的点”四个维度记录下来。不想写详细文字的话至少把题目关键字和代码草稿留存下来睡觉前再对着草稿过一遍。整理完题面之后务必要把没AC的题重新做一遍。我那个带限制签到的DP题笔试结束后我花了半小时重新推导很快发现dp初始值的问题修改后自己构造了几组用例全部通过。那一刻我的感觉是如果笔试前对这种“初始值必须为负无穷”的DP题有更强的敏感度现场完全可能AC。这就是复盘的价值它会把你的知识漏洞精准暴露出来而不是让你盲目刷题。5.2 笔试到面试的衔接怎么把这次笔试变成面试素材笔试结束后接下来就是等结果、准备面试。这里有一个很多人没意识到的技巧笔试中遇到的场景设计题和编程题完全可以拿来当面试项目经验中的素材。比如笔试考了“滑动窗口内最大播放热度”你可以在面试被问到“你项目里怎么做实时数据统计”时把单调队列的思想延伸成“我了解过基于滑动窗口的实时热度计算方案”哪怕没有实际操作也能展现你对实时计算场景的理解。面试基础八股方面笔试里考的TCP握手、Redis缓存雪崩、MySQL索引失效、消息队列可靠性几乎都会在二面或三面里换着花样再问一遍。所以笔试基础题就是面试内容的“预告片”。我的习惯是把笔试里每道选择题的每个选项都当成一个面经题选对了也要回去看一遍其他选项为什么错选错了更是重点标记。这样一轮笔试下来相当于免费拥有了几十个高频面试考点。5.3 对标正式offer项目经历与音乐业务场景的匹配到了面试环节腾讯音乐对你的项目经历会很看重“匹配度”。不需要你做过音乐App但如果你能在项目描述里体现出对高并发、推荐系统、播放性能等方向的理解会更容易获得认可。我自己的建议是把项目经历按“业务背景—技术架构—个人贡献—难点解决—量化结果”来整理并且提前准备两个场景问题“如果让你设计一个日活千万的音乐排行榜你会怎么做”“用户播放一首歌后端要经过哪些核心链路”。这两个问题都指向音乐业务的核心提前思考过面试时就不会临场语无伦次。5.4 给备战后续批次的同学的建议如果你还没参加笔试正在等下一批我的建议是三条。第一把LeetCode高频题按“数组、字符串、哈希、双指针、滑动窗口、二叉树、DP、贪心、堆、单调栈”分类刷每种题型至少熟练掌握两个模板做到看到题目就能反应出对应算法。第二八股部分不要背一堆零散概念要能用一条“用户点击播放歌曲”的链路把网络、操作系统、数据库、缓存、消息队列串起来。第三每周至少参加一次模拟笔试用整块时间限时训练习惯在压力下快速审题和放弃难题。其实把整场笔试拆开看每一类题单独拿出来难度都不算太高难的是在120分钟内同时调动算法、基础知识和业务思维。我个人的体会是腾讯音乐这套笔试题的筛选逻辑并不是“找算法竞赛冠军”而是“找基本功扎实、能写业务代码、对音乐场景有感知的人”。所以你不需要焦虑自己是不是ACM选手把高频模块练扎实把基础八股吃透完全有机会走完全程。希望这篇复盘能帮大家少走一点弯路祝每一份投递都得到回音。