
2023年秋招那會兒我投了騰訊音樂的研發崗崗位是后端方向。筆試用的是賽碼網限時120分鐘題型是選擇題單選多選加3道編程題。說實話在牛客網上刷過不少大廠筆試但賽碼網這個平臺當時用的人不算多環境陌生、輸入輸出格式又和平時練的不太一樣導致我開場那幾分鐘非常難受。這篇復盤我把整場筆試從收到郵件到交卷的每個環節都過一遍包括題目類型、踩坑點、時間分配和后續面試銜接給準備走大廠研發崗秋招的同學做個參考。1. 收到筆試郵件之后我如何判斷這場筆試的考察重點1.1 從崗位描述反推考點騰訊音樂研發崗的JD寫得比較寬泛核心要求是扎實的計算機基礎、掌握至少一門服務端語言Java/C/Go都行、熟悉常見數據結構和算法。但寬泛不等于沒重點我投的是TME的后臺研發筆試前我給自己圈定了四個考察方向算法與數據結構、計算機網絡、操作系統、數據庫。另外考慮到騰訊音樂的業務場景是音視頻和社交娛樂字符串處理、高頻查詢類問題比如排行榜、搜索建議出題概率會高于純粹的業務CRUD題。這個判斷基本被我后續在牛客和小紅書上看到的同批筆試回憶印證了。2023年騰訊系大廠筆試普遍壓縮了選擇題比重把大頭放在編程題上三題通常按難度遞進第一題簽到性質考察基本編碼能力和邊界處理第二題中等常見動態規劃或貪心第三題偏難圖論、狀態壓縮或復雜模擬。騰訊音樂這場大致也是這個節奏所以我從收到郵件那天起就把復習重心放在LeetCode Hot 100和中高難度的動態規劃、圖論題上。1.2 賽碼網筆試環境的提前適配很多同學忽視一個關鍵點賽碼網和牛客網的在線IDE處理方式完全不一樣。牛客網代碼框內寫題自動保存輸入輸出已經幫你處理好了。賽碼網雖然也內置編輯器但它在瀏覽器兼容性、輸入格式、內存報錯提示上都更“原生”一些——意思就是你更接近在本地IDE里裸寫代碼沒有那么多智能提示。我提前兩天去賽碼網上找了幾套模擬題練手主要做了三件事第一確認它支持哪些語言版本我用的是C確認支持C17可以用一些新特性第二測試multi-case輸入模板賽碼網很多題目不會像LeetCode那樣給你封裝好函數而是要求自己讀while (cin n)這種循環第三驗證本地編譯調試的流程賽碼網允許在編輯器里寫代碼再提交但調試信息輸出到日志區偶爾有延遲。這三天準備幫我避開了最大的坑題目能做出來但因為輸入輸出格式不正確導致0分。下面細說。2. 筆試現場全流程復盤選擇、多選與一道差點翻車的模擬題2.1 選擇題部分的時間分配與心態整場筆試120分鐘我的計劃是選擇題控制在40分鐘以內剩下80分鐘全部留給編程題。實際執行時稍微超了一點用了45分鐘。原因是今年的選擇題里有一道操作系統相關的題涉及內存分頁和頁表項大小計算我自己對這部分不夠熟來回算了兩遍。我當時的策略是單選快速過遇到不確定的先用排除法選一個標記一下多選保守處理只選有把握的選項。多選的分值通常較高少選還可能拿部分分但錯選直接零蛋所以不建議在多選上賭。比如有道題問“哪些協議屬于應用層”我確定了HTTP和DNSSMTP也確定但不太確定TFTP我就只選了三個很確定的寧可少拿一分也不冒險。2.2 記憶中的幾道關鍵選擇題與答案解析這里復盤三道我印象最深的題也串一下背后的考點。第一道是TCP擁塞控制。題目給了兩個TCP連接問某一時刻擁塞窗口大小的變化涉及到慢啟動、擁塞避免、快速重傳、快速恢復四個階段的切換條件。這道題本身不難但選項里埋了一個陷阱慢啟動變成擁塞避免的閾值在快速恢復之后會被設置為當前擁塞窗口的一半很多同學會把這個閾值變動記錯。我的經驗是把TCP擁塞控制理解為“先翻倍再線性加遇超時重置遇三個ACK減半”的十六字口訣再通過畫時間線圖來解。第二道是C虛函數表的布局。題目描述了一個基類和派生類的繼承結構各有虛函數和普通成員變量問對象內存布局中虛函數指針放在哪里、派生類新增虛函數怎么填充。這道題我在復習時專門整理過多繼承下虛函數表的排列規則所以答得比較快。核心結論是單繼承下對象頭部放一個虛表指針指向虛函數表多繼承下每一條繼承鏈都對應一個虛表指針新增虛函數追加到第一條繼承鏈的虛函數表末尾。我后來在面試中還遇到過類似問題確認這塊是非常重要的內存布局基礎。第三道是一道“偽”算法題考LRU緩存。它沒有直接問LRU實現而是給了一個基于數組時間戳的LRU近似實現問某次訪問后緩存里的元素順序。這道題很多人看到LRU就默認用哈希表雙向鏈表反而忽視了題目里給定的具體數據結構。我在這里停頓了大概三分鐘最后老老實實按其時間戳規則一步步模擬。應對技巧是看到“XX算法”的題優先回到題目的字面實現而不是條件反射套經典解法。2.3 一道讓我猶豫很久的模擬題這類選擇題最考驗臨場思路。題目大意是一個3級頁表系統頁大小4KB頁表項4字節進程虛擬地址空間大小4GB物理內存1GB問這個進程頁表總共占多少內存選項是從幾MB到幾十MB的差別。我計算邏輯是虛擬地址4GB按4KB頁劃分共1M個頁。頁表項需要4字節一級頁表可索引1K個頁表項所以一級頁表有1K個頁表項對應二級頁表有1K個三級頁表有1M個。頁表總項數大約是1M頁表項 1K二級頁表 1K一級頁表不對準確說如果是多級頁表每一級頁表大小是4KB共有1K1K1M個頁表項這樣算完大概4MB多。但選項里有個接近但不同的數值我一開始沒看內存對齊。后面我重新梳理了一遍3級頁表頁大小4KB地址空間4GB則虛擬地址劃分成10位10位12位頁表一級和二級各有1K項三級有1M項。頁表項大小是4字節三級頁表總大小是1M×4字節4MB二級頁表總大小是1K×4字節×1K個二級頁表4MB一級頁表是4KB×1K個一級頁表其實還需要考慮每個進程只映射一部分物理內存。我最后選了一個我認為合理的但說實話這題消耗了我比較多時間也暴露了我對多級頁表深層計算不夠熟練。如果提前把《深入理解計算機系統》里虛擬內存章節的例題刷一遍會從容很多。3. 三道編程題從題目難度梯度到細節陷阱編程題部分基本上決定了你能不能進面試因為選擇題大家差距不大但編程題AC的題數會拉開檔次。我這次三題AC了前兩題第三題只過了部分用例最后拿到了面試資格。下面按我實操順序講。3.1 第一題字符串重排簽到題也有邊界坑題目大意為給定一個由小寫字母組成的字符串和一個整數k要求重排字符串使得任意兩個相同字符之間的距離至少為k。如果無法完成輸出空串或某個約定值。這和LeetCode 358“K距離間隔重排字符串”幾乎是同一道題屬于典型的貪心優先隊列。我當時的思路是統計每個字符出現次數用大頂堆維護出現次數字符對。每次從堆中取出出現次數最多的字符放入結果并把它暫存起來等距離滿足k之后再重新放回堆中。如果堆為空但結果長度還不夠說明無法完成。核心代碼框架#include bits/stdc.h using namespace std; string rearrange(string s, int k) { if (k 1) return s; unordered_mapchar, int cnt; for (char c : s) cnt[c]; priority_queuepairint, char pq; for (auto [ch, num] : cnt) pq.push({num, ch}); string res; queuepairint, char wait; while (!pq.empty()) { auto [num, ch] pq.top(); pq.pop(); res.push_back(ch); wait.push({num - 1, ch}); if ((int)wait.size() k) { auto [n, c] wait.front(); wait.pop(); if (n 0) pq.push({n, c}); } } return (int)res.size() (int)s.size() ? res : ; }這題我調試了大概8分鐘主要坑在于wait.size() k不是 k因為排隊隊列里可能長度超過k。另外如果字符串里某個字符重復次數特別大而且k非常大隊列永遠推不回堆里最后結果長度必然小于原字符串長度直接返回空串。這個邊界判斷非常關鍵我第一次提交時只判斷了堆空沒有在最后比對長度導致一個測試用例不過。補上res.size() s.size()的校驗后AC。3.2 第二題動態規劃狀態設計差點想歪第二題是一道典型的線性DP大意是給定一個數組每次可以移除一個元素得分為該元素乘以其左右相鄰元素的積如果相鄰元素被移除則跳過問移除到剩下兩個元素時最大得分。這個題是“移除盒子”的減化版本也可以用區間DP做。我一開始想的貪心是每次移除得分最小的元素。試了一組數據發現不對比如數組[3, 1, 5, 8]貪心移除1得分1×3×515再移除3得分3×5×8120再移除5得分5×8總和很大但最優解并不一定是這個順序。之后我轉向區間DP定義dp[i][j]為區間(i, j)內元素全部移除后的最大得分當然最后還要考慮邊界情況。轉移方程參考“戳氣球”的思路枚舉區間內最后一個被移除的元素kdp[i][j] max(dp[i][j], dp[i][k] dp[k][j] nums[i]*nums[k]*nums[j])其中nums[i]和nums[j]是區間兩端保留的元素。這題和LeetCode 312“戳氣球”高度相似只是邊界條件不同所以其實并不算難但狀態設計如果一開始沒往區間DP方向想很容易卡住。我提交后有一組測試點超時發現是區間長度從小到大的循環寫成從大到小導致大量重復計算。調換循環順序后順利通過。這里也驗證了一個經驗DP題如果狀態定義正確但出現超時先檢查循環順序是不是從底向上。3.3 第三題圖論狀態壓縮部分分策略是明智的選擇第三題明顯是壓軸題題目是一個帶權無向圖每個節點有顏色比如紅藍綠要求找到一條從起點到終點的路徑使得路徑上每種顏色至少出現一次且路徑總權重最小。這題不要求經過所有節點但要求顏色覆蓋。我一看數據范圍節點數n≤1e5邊數m≤2e5顏色種類≤3就知道這題不能裸跑BFS狀態壓縮。正確的打開方式應該是從起點和終點分別跑一次單源最短路Dijkstra記錄每個點到達起點、到達終點時攜帶的顏色集合狀態然后枚舉中間點合并兩邊狀態如果合并后顏色全集為所需集合則更新答案。復雜度O((nm)logn * 2^k)k是顏色種類這里k3可行。但我當時犯了兩個錯誤一是把Dijkstra的距離數組定義成了二維dist[node][colorMask]沒有意識到其實只需要兩個一維數組加狀態枚舉二是用了鄰接矩陣而不是鄰接表導致內存直接爆掉。等我想明白優化方案時時間只剩下20分鐘我果斷選擇寫一個帶狀態壓縮的BFS暴力版本先跑通小數據拿部分分并確認算法思路在題目給定的小樣例上正確。最終結果暴力版本過了約60%的測試點拿到了一個還可以的分數。這個選擇我認為是明智的。大廠筆試編程題不一定要求全AC很多時候兩題半就能進面關鍵是不要在第三題上死磕導致沒有時間檢查前面交上去的代碼是否有低級錯誤。我當時的策略是先提交一版能跑出正確答案但可能超時的代碼保住正確性分數再嘗試優化沒時間優化就算了。4. 騰訊音樂研發崗筆試的知識點橫向串聯4.1 算法不是只刷LeetCode就能過很多同學背題式刷LeetCode看到題目類型眼熟就以為自己會了。但大廠筆試的算法題往往會在經典題上做一層業務包裝比如第一題的“K距離間隔重排”直接就是LeetCode原題變體第二題是“戳氣球”的換皮第三題則是把最短路徑和狀態壓縮結合。只靠背題是不行的你需要理解底層算法的推導過程。我建議準備時把LeetCode上經典題按類型整理成思維導圖滑動窗口、雙指針、單調棧、區間DP、狀態壓縮DP、圖論最短路、最小生成樹、拓撲排序等。每個類型至少精做3~5題而且每題都要能默寫核心代碼而不是看一遍題解就過。另外騰訊系筆試特別愛考“環狀數組”“字符串約簡”“區間覆蓋”這類能體現思維靈活度的題。這些題本身算法難度不高但細節多返回值要求多容易在邊界條件翻車。平時練習時一定要自己構造幾組極端數據空數組、全相等、單元素、負數、溢出把這些測試用例跑通再提交。4.2 C/Java語言細節編譯型崗位和JVM崗位的考察差異研發崗筆試的選擇題里語言相關題目占比不低具體考哪門取決于你投遞的崗位語言棧。C崗位會考虛函數、內存對齊、智能指針、移動語義、模板特化Java崗位會考JVM內存分區、垃圾收集器、HashMap底層原理、并發工具類等。騰訊音樂的后臺開發可C可Java我投的偏C方向所以重點復習了C11/14/17的新特性。我印象深刻的一道題是考shared_ptr的線程安全性。題目問多個線程同時拷貝同一個shared_ptr對象是否線程安全答案是引用計數本身是原子操作所以拷貝是安全的但多個線程同時修改同一個shared_ptr比如賦值重置則不安全需要額外加鎖。這個點非常容易被誤判因為在Java語境下大家習慣說HashMap線程不安全但C智能指針的線程安全邊界比較微妙面試官特別喜歡拿這種邊界題來篩人。如果你還有時間我建議把Effective Modern C里關于智能指針、移動語義、lambda捕獲的條款過一遍再結合《深入理解計算機系統》第3章程序機器級表示來理解內存布局因為這兩塊內容在筆試、面試中反復出現。4.3 網絡、操作系統、數據庫筆試小題背后的工程價值選擇題里的網絡和操作系統題表面上看是筆試過場實際上它們在后續面試中會被深挖成項目場景題。比如TCP擁塞控制那道題如果你只是記住了四個階段的名字面試官會繼續問“如果網絡出現頻繁超時你會如何調整擁塞窗口”再比如Lru緩存題面試官會順著問“如果并發訪問很高LRU如何加鎖有沒有鎖粒度更小的實現”我的經驗是復習選擇題時不要只記結論要把每個結論還原成場景。TCP為什么需要慢啟動因為剛建立連接時并不知道網絡可用帶寬有多大貿然發送大量數據可能會造成網絡擁塞。LRU為什么用哈希表雙向鏈表因為需要O(1)時間完成查找和刪除數組時間戳的樸素實現雖然查得快但刪除和插入效率太低。數據庫方面騰訊音樂的業務場景是排行榜、歌單、用戶關注關系這些涉及大量讀多寫少的高QPS查詢所以索引設計、B樹結構、覆蓋索引、分庫分表是高頻考點。選擇題如果考“什么情況下索引會失效”至少要能舉出四五個例子左模糊查詢、對索引列使用函數、隱式類型轉換、聯合索引不滿足最左前綴原則等。5. 筆試之后的復盤哪些分本可以不丟5.1 賽碼網特有的雷我幫你踩過了賽碼網這個平臺如果你之前沒在上面練過有四個雷必須提前排除。第一它不自動保存代碼。如果頁面意外刷新你寫了40分鐘的代碼可能直接消失所以養成“寫一段就手動復制到本地記事本”的習慣。我筆試時雖然沒遇到掉線但聽說同批有人因為切出頁面太久被判作弊或者代碼丟失非常狼狽。第二輸入輸出模板要提前準備。賽碼網很多題是ACM風格需要你處理多行輸入甚至輸入里第一行是測試用例數T后面跟著T組數據。如果不知道自己寫的是while (cin n)還是for (int i0; iT; i)很容易在本地跑通但提交后0分。第三編譯器版本選擇和本地不太一致。賽碼網C默認支持C17但有些老的OJ模板只支持C11。如果你用了結構化綁定或者auto返回值推導等新特性可能導致編譯失敗。提交前先確認語言版本或者干脆用C11的保守寫法。第四內存超限的提示不直觀。如果你動態申請了超大數組賽碼網可能會顯示“答案錯誤”而不是“內存超限”導致你誤以為算法本身有問題反復改邏輯浪費時間。遇到大數組時先估算內存占用是否超過限制再考慮算法優化。5.2 時間分配的量化模型筆試復盤后我總結了一套“120分鐘通用分配模型”后續在京東、美團、拼多多的筆試里也用了體感不錯。前10分鐘快速瀏覽全部題目不急著動筆。用這三道題把你腦內的題單過一遍這是見過的題型還是沒見過的新題型如果見過直接回憶算法模板如果沒見過先跳過做下一題。40分鐘做選擇題平均每題2分鐘超過3分鐘的先標記跳過。多選不要貪只選確定項。55分鐘做編程題前兩題每道題最多25分鐘包括讀題、思考、編碼、自測。15分鐘做第三題。如果前兩題還沒AC先保第一題不要戀戰。最后10分鐘檢查已經提交的代碼是否有多余調試輸出、輸入格式是否有問題、是否忘了return 0。這個模型的邏輯是編程題前兩題的分值比第三題高而第三題往往是給少數人準備的區分題。目標不是滿分而是穩定拿到前80%的分數。5.3 后續面試銜接筆試題目如何成為面試素材大廠面試經常會圍繞你的筆試答題記錄提問尤其是你沒AC的那道題。面試官不會因為你沒做出來就掛你反而會想看看你的思路完整度和臨場反應。所以筆試結束后我立刻把三題重新做了一遍整理成筆記包含題目描述、我的原始思路、錯誤卡點、標準解法和復雜度分析。后面一面時面試官確實問到了第三題“你筆試時第三題只過了部分用例現在能講講思路嗎”因為我有現成整理整個回答行云流水這一輪印象分直接拉滿。這個整理文檔建議用Markdown或Notion保存每道題包含以下部分題目類型和知識點標簽輸入輸出格式原始解題思路和為什么不對正確思路和復雜度手寫的邊界測試用例不要忽略這個環節它可能決定你從“筆試通過”到“面試通過”的那一步。6. 一些額外的話筆試只是秋招長跑中的一站過了自然開心沒過大不了再投下一家。但復盤一次筆試比盲目刷十道新題更有價值。我在這次騰訊音樂筆試里真正學到的東西不是某道題的解法而是如何管理90分鐘內的緊張感、如何在完全陌生的OJ環境下穩定輸出、如何在拿到三道難題時快速判斷放棄哪一道。這些能力在后來的拼多多筆試和美團面試里都派上了用場。如果你現在正處于秋招準備期我的建議是每周抽一天做整套的全真模擬平臺就用賽碼網或牛客企業題庫掐表、全程不切瀏覽器、不翻筆記模擬到能穩定輸出為止。等上了真正的考場你會發現最難的往往不是題目本身而是如何在有限時間內把自己會的內容全部轉化成分數。