研發(fā)工程師模擬筆試題復(fù)盤(pán):從數(shù)據(jù)結(jié)構(gòu)到高并發(fā)系統(tǒng)設(shè)計(jì))
模擬筆試題這種東西往往有個(gè)奇怪的現(xiàn)象你越臨近筆試越想找“原題”和“押題”但真正拉開(kāi)差距的從來(lái)不是那幾道沒(méi)見(jiàn)過(guò)的題而是你對(duì)基礎(chǔ)知識(shí)的理解深度。我最近翻到這套美團(tuán)2016年的研發(fā)工程師模擬筆試題說(shuō)實(shí)話第一眼覺(jué)得題目有點(diǎn)“老”但逐題做下來(lái)反而覺(jué)得比現(xiàn)在很多花哨的面經(jīng)更有參考價(jià)值——它很誠(chéng)實(shí)地反映了大廠研發(fā)崗考察的基本盤(pán)數(shù)據(jù)結(jié)構(gòu)與算法、操作系統(tǒng)、網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)外加一點(diǎn)邏輯思維。如果你正準(zhǔn)備投遞美團(tuán)或者其他互聯(lián)網(wǎng)公司的研發(fā)崗位這套模擬題能幫你快速自測(cè)基礎(chǔ)扎不扎實(shí)、代碼功底夠不夠、遇到?jīng)]見(jiàn)過(guò)的場(chǎng)景題會(huì)不會(huì)懵。這篇文章我不打算只貼答案而是把每一類(lèi)題背后的考察邏輯、解題思路、容易踩的坑都拆開(kāi)講一遍順便補(bǔ)充一些我在實(shí)際面試和工作中總結(jié)的經(jīng)驗(yàn)。哪怕你不考美團(tuán)這套題背后的能力模型也是通用的。1. 2016年的模擬題為什么放到今天仍然值得做1.1 先搞清楚這套題出現(xiàn)的行業(yè)背景2016年前后的美團(tuán)正處于業(yè)務(wù)高速擴(kuò)張期。外賣(mài)、到店餐飲、酒旅、電影票多條業(yè)務(wù)線同時(shí)推進(jìn)技術(shù)團(tuán)隊(duì)規(guī)模迅速增長(zhǎng)。這個(gè)階段的大廠筆試承擔(dān)的核心任務(wù)不是“選天才”而是“高效篩掉基礎(chǔ)不過(guò)關(guān)的人”——投遞簡(jiǎn)歷的人太多必須用一套標(biāo)準(zhǔn)化題目快速過(guò)濾出具備基本工程素養(yǎng)的候選人。所以你會(huì)發(fā)現(xiàn)這套模擬題幾乎沒(méi)有偏題怪題全部落在計(jì)算機(jī)基礎(chǔ)知識(shí)的主干道上。這恰恰是它到今天仍然有價(jià)值的原因基礎(chǔ)能力永遠(yuǎn)是研發(fā)崗位的第一道門(mén)檻不管業(yè)務(wù)怎么變這一關(guān)沒(méi)有繞過(guò)去的捷徑。1.2 這套模擬題真實(shí)想考察的能力維度我做了幾年的技術(shù)面試官回頭看這類(lèi)筆試題其實(shí)它想考察的底層能力只有四個(gè)維度代碼基本功能否在有限時(shí)間內(nèi)寫(xiě)出語(yǔ)法正確、邏輯完整、邊界清晰的代碼。算法思維能否識(shí)別題目背后的數(shù)據(jù)結(jié)構(gòu)與算法模型給出合理的時(shí)間復(fù)雜度方案。知識(shí)體系完整性操作系統(tǒng)、網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)這些日常開(kāi)發(fā)繞不開(kāi)的基礎(chǔ)知識(shí)是否形成了體系化的理解而不是碎片化的記憶。場(chǎng)景拆解能力面對(duì)一個(gè)實(shí)際業(yè)務(wù)問(wèn)題能否把它拆解成可計(jì)算的子問(wèn)題并選擇合適的技術(shù)手段。這四個(gè)維度在今天的技術(shù)面試中依然是核心。所以別抱著“這套題太老沒(méi)有參考價(jià)值”的心態(tài)把它當(dāng)成一套自測(cè)題比盲目刷一堆新題更能幫你找準(zhǔn)自己的薄弱環(huán)節(jié)。2. 數(shù)據(jù)結(jié)構(gòu)和算法題拆解每一道題都在考什么2.1 動(dòng)態(tài)規(guī)劃題硬幣找零與配送場(chǎng)景的結(jié)合先看一道很有代表性的題給定不同面額的硬幣 coins 和一個(gè)總金額 amount編寫(xiě)一個(gè)函數(shù)計(jì)算可以湊成總金額所需的最少的硬幣個(gè)數(shù)。如果沒(méi)有任何一種硬幣組合能組成總金額返回 -1。這道題在2016年出現(xiàn)本質(zhì)上考察的是動(dòng)態(tài)規(guī)劃的基礎(chǔ)思維。當(dāng)年很多候選人會(huì)陷入貪心算法的陷阱先拿大面額硬幣去湊湊不出來(lái)再換小面額。但貪心在硬幣面額不滿足整除關(guān)系時(shí)比如面額為 1、3、4總金額為 6會(huì)得到錯(cuò)誤答案。正確做法是建立狀態(tài)轉(zhuǎn)移方程。定義dp[i]為湊成金額 i 所需的最少硬幣數(shù)那么dp[i] min(dp[i - coins[j]] 1) 其中 coins[j] i初始化dp[0] 0其他為無(wú)窮大。最終如果dp[amount]仍為無(wú)窮大說(shuō)明無(wú)法湊成返回 -1。以下是一個(gè)樸素的實(shí)現(xiàn)int coinChange(vectorint coins, int amount) { vectorint dp(amount 1, INT_MAX); dp[0] 0; for (int i 1; i amount; i) { for (int j 0; j coins.size(); j) { if (coins[j] i dp[i - coins[j]] ! INT_MAX) { dp[i] min(dp[i], dp[i - coins[j]] 1); } } } return dp[amount] INT_MAX ? -1 : dp[amount]; }復(fù)雜度為 O(amount * coins.size())。這里要注意dp數(shù)組需要用amount 1的長(zhǎng)度因?yàn)榻痤~從 0 開(kāi)始計(jì)算同時(shí)必須判斷dp[i - coins[j]]是否可達(dá)否則INT_MAX 1會(huì)發(fā)生整型溢出。我補(bǔ)充一個(gè)實(shí)際業(yè)務(wù)聯(lián)想2016年外賣(mài)配送場(chǎng)景中騎手?jǐn)y帶的零錢(qián)有限需要快速計(jì)算如何用給定面額湊出找零金額本質(zhì)上就是這類(lèi)問(wèn)題。雖然真實(shí)系統(tǒng)會(huì)有更復(fù)雜的約束比如每種硬幣數(shù)量有限但核心思維完全一致。如果你在筆試中能主動(dòng)說(shuō)出“這個(gè)問(wèn)題在業(yè)務(wù)中可以對(duì)應(yīng)到找零場(chǎng)景”面試官會(huì)認(rèn)為你有業(yè)務(wù)敏感度這是加分項(xiàng)。2.2 鏈表題反轉(zhuǎn)鏈表的迭代與遞歸寫(xiě)法有一道高頻手寫(xiě)題是反轉(zhuǎn)單鏈表。題目描述非常簡(jiǎn)單反轉(zhuǎn)一個(gè)單鏈表。別小看這道題。它考察的是指針操作的熟練度和鏈表這個(gè)數(shù)據(jù)結(jié)構(gòu)的基本功。迭代寫(xiě)法的關(guān)鍵是用三個(gè)指針prev、current、next完成原地反轉(zhuǎn)ListNode* reverseList(ListNode* head) { ListNode* prev nullptr; ListNode* curr head; while (curr ! nullptr) { ListNode* next curr-next; curr-next prev; prev curr; curr next; } return prev; }這里有個(gè)細(xì)節(jié)必須先保存curr-next否則一旦修改了curr-next指向原鏈表就斷了后序節(jié)點(diǎn)全部丟失。這個(gè)錯(cuò)誤我見(jiàn)過(guò)無(wú)數(shù)候選人犯。遞歸寫(xiě)法要理解一個(gè)核心思想假設(shè)當(dāng)前節(jié)點(diǎn)之后的鏈表已經(jīng)反轉(zhuǎn)完成只需讓當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)指回當(dāng)前節(jié)點(diǎn)再斷開(kāi)當(dāng)前節(jié)點(diǎn)與下一個(gè)節(jié)點(diǎn)的連接ListNode* reverseList(ListNode* head) { if (head nullptr || head-next nullptr) { return head; } ListNode* newHead reverseList(head-next); head-next-next head; head-next nullptr; return newHead; }遞歸寫(xiě)法的代碼更短但理解門(mén)檻更高。筆試時(shí)如果時(shí)間緊張我建議寫(xiě)迭代版本不容易出錯(cuò)面試官也更熟悉。在真實(shí)面試中這道題最常見(jiàn)的追問(wèn)是“如果鏈表有環(huán)你的代碼會(huì)怎樣”這就涉及快慢指針檢測(cè)環(huán)的知識(shí)最好提前準(zhǔn)備好。2.3 字符串題最長(zhǎng)無(wú)重復(fù)字符子串的滑動(dòng)窗口解法還有一道經(jīng)典題也值得復(fù)盤(pán)給定一個(gè)字符串找出其中不含有重復(fù)字符的最長(zhǎng)子串的長(zhǎng)度。這道題在2016年的筆試中出現(xiàn)頻率很高。最直觀的暴力解法是枚舉所有子串并檢查是否包含重復(fù)字符時(shí)間復(fù)雜度 O(n^3)在面試中基本不具備可行性。正確的解法是滑動(dòng)窗口。用兩個(gè)指針left和right維護(hù)一個(gè)窗口right不斷向右擴(kuò)展并將遇到的字符存入哈希集合如果發(fā)現(xiàn)當(dāng)前字符已經(jīng)在集合中則移動(dòng)left逐步縮小窗口直到將該字符移出集合。窗口的最大長(zhǎng)度就是答案。int lengthOfLongestSubstring(string s) { unordered_setchar window; int left 0, right 0; int maxLen 0; while (right s.length()) { if (window.find(s[right]) window.end()) { window.insert(s[right]); maxLen max(maxLen, right - left 1); right; } else { window.erase(s[left]); left; } } return maxLen; }注意left移動(dòng)的邏輯不是一次性把left跳到重復(fù)字符的下一個(gè)位置而是一步步移動(dòng)并逐個(gè)刪字符。這種寫(xiě)法雖然多了一些循環(huán)次數(shù)但邏輯簡(jiǎn)單、不易出錯(cuò)。如果你追求更優(yōu)寫(xiě)法可以用哈希表記錄每個(gè)字符最近一次出現(xiàn)的位置讓left直接跳轉(zhuǎn)寫(xiě)法會(huì)更緊湊。這道題背后的核心能力是“滑動(dòng)窗口”這個(gè)雙指針技巧的靈活運(yùn)用。在真實(shí)的日志分析、流量削峰、字符串匹配等場(chǎng)景中滑動(dòng)窗口思想非常實(shí)用。筆試中如果時(shí)間允許最好在代碼中用注釋標(biāo)注你的思路面試官能從中看到你的結(jié)構(gòu)化思考能力。3. 操作系統(tǒng)與網(wǎng)絡(luò)基礎(chǔ)拉開(kāi)差距的隱藏考點(diǎn)3.1 進(jìn)程與線程的區(qū)別不要只背定義很多候選人答“進(jìn)程與線程的區(qū)別”時(shí)只會(huì)背“進(jìn)程是資源分配的最小單位線程是CPU調(diào)度的最小單位”然后就說(shuō)不出更多了。這套模擬題里有一道類(lèi)似的題目其實(shí)想考察的是你對(duì)并發(fā)模型的理解深度。我的建議是從三個(gè)層面回答。資源維度進(jìn)程擁有獨(dú)立的地址空間、文件描述符、信號(hào)處理器等資源同一進(jìn)程內(nèi)的線程共享這些資源。這意味著線程間通信成本更低但同步問(wèn)題更復(fù)雜。調(diào)度維度進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的基本單位線程是CPU調(diào)度的基本單位。線程的上下文切換比進(jìn)程輕量因?yàn)樗恍枰袚Q地址空間。故障隔離維度一個(gè)進(jìn)程崩潰通常不影響其他進(jìn)程但一個(gè)線程崩潰可能導(dǎo)致整個(gè)進(jìn)程退出進(jìn)而影響同一進(jìn)程內(nèi)的所有線程。補(bǔ)充一個(gè)實(shí)際場(chǎng)景在2016年外賣(mài)訂單處理系統(tǒng)中如果每個(gè)訂單請(qǐng)求創(chuàng)建一個(gè)進(jìn)程資源開(kāi)銷(xiāo)會(huì)非常大因?yàn)檫M(jìn)程創(chuàng)建和上下文切換的成本遠(yuǎn)高于線程。所以服務(wù)端通常采用多線程模型或者事件驅(qū)動(dòng)模型來(lái)處理高并發(fā)請(qǐng)求。這就是面試官期待的綜合分析能力而不只是背誦定義。3.2 死鎖的四個(gè)必要條件與實(shí)際案例死鎖相關(guān)題目幾乎是操作系統(tǒng)部分的???。四個(gè)必要條件必須能脫口而出互斥條件、持有并等待條件、不可剝奪條件、循環(huán)等待條件。重要的是能結(jié)合實(shí)例說(shuō)明。以經(jīng)典的數(shù)據(jù)庫(kù)訂單表更新為例事務(wù)A持有訂單表某行的鎖等待更新用戶表事務(wù)B持有用戶表的鎖等待更新訂單表。兩個(gè)事務(wù)互相等待誰(shuí)也無(wú)法完成這就是死鎖。破解死鎖的思路有兩種一是破壞必要條件比如用超時(shí)機(jī)制讓事務(wù)主動(dòng)釋放鎖破壞持有并等待或不可剝奪條件二是保證所有事務(wù)按固定順序加鎖避免循環(huán)等待。筆試中如果出到這類(lèi)題建議畫(huà)一個(gè)簡(jiǎn)單的資源分配圖輔助說(shuō)明即使不能畫(huà)圖也要在文字里清晰描述“誰(shuí)持有、誰(shuí)等待”的循環(huán)關(guān)系。3.3 TCP三次握手與HTTP狀態(tài)碼的應(yīng)用理解網(wǎng)絡(luò)部分TCP三次握手是必考基礎(chǔ)。按標(biāo)準(zhǔn)答案回答“SYN、SYNACK、ACK”只是及格線更高階的回答要說(shuō)明為什么需要三次握手。核心原因在于需要確認(rèn)雙方的收發(fā)能力是否正常并同步初始化序列號(hào)。如果只有兩次握手服務(wù)端無(wú)法確認(rèn)客戶端的接收能力是否正常如果四次握手則中間存在可以合并的冗余步驟。狀態(tài)碼部分有兩類(lèi)容易被忽略一類(lèi)是301與302的區(qū)別另一類(lèi)是401與403的區(qū)別。301是永久重定向302是臨時(shí)重定向401是未認(rèn)證403是已認(rèn)證但無(wú)權(quán)限。實(shí)操中美團(tuán)這類(lèi)大廠在登錄失效時(shí)通常會(huì)返回401或自定義的登錄態(tài)失效碼前端拿到后跳轉(zhuǎn)登錄頁(yè)。你能在筆試題里把狀態(tài)碼和真實(shí)業(yè)務(wù)行為對(duì)應(yīng)起來(lái)就說(shuō)明你不是死記硬背。2016年移動(dòng)端場(chǎng)景用戶的手機(jī)網(wǎng)絡(luò)不穩(wěn)定時(shí)App發(fā)起的HTTP請(qǐng)求可能出現(xiàn)連接超時(shí)、請(qǐng)求重發(fā)、響應(yīng)亂序等問(wèn)題。這背后涉及TCP超時(shí)重傳、HTTP冪等性設(shè)計(jì)等知識(shí)。筆試中遇到這類(lèi)題如果能提到“重試與冪等”的解決方案面試官會(huì)眼前一亮。4. 數(shù)據(jù)庫(kù)與系統(tǒng)設(shè)計(jì)思維從索引到高并發(fā)扣減4.1 索引為什么失效常見(jiàn)場(chǎng)景全梳理數(shù)據(jù)庫(kù)索引失效是研發(fā)崗位筆試和面試的高頻考點(diǎn)。模擬題中通常會(huì)給出幾個(gè)SQL語(yǔ)句讓你判斷索引是否生效。我把常見(jiàn)的索引失效場(chǎng)景整理成一個(gè)清單場(chǎng)景原因示例對(duì)索引列使用函數(shù)函數(shù)導(dǎo)致無(wú)法利用B樹(shù)有序性WHERE YEAR(create_time) 2024隱式類(lèi)型轉(zhuǎn)換字符串列與數(shù)字比較時(shí)發(fā)生轉(zhuǎn)換WHERE phone 13800138000phone 是 varchar前綴模糊匹配最左匹配原則不滿足WHERE name LIKE %張使用 OR 連接非索引列優(yōu)化器可能選擇全表掃描WHERE id 1 OR age 20聯(lián)合索引不滿足最左前綴聯(lián)合索引的匹配順序索引(a,b)條件只寫(xiě)b 1索引列參與計(jì)算破壞索引列原始值WHERE salary * 2 10000這些知識(shí)點(diǎn)光背沒(méi)用最好在本地用真實(shí)數(shù)據(jù)庫(kù)實(shí)驗(yàn)一遍。你可以創(chuàng)建一張十萬(wàn)行數(shù)據(jù)的表分別用以上幾種方式查詢用EXPLAIN看執(zhí)行計(jì)劃觀察type列從const或ref變成ALL就會(huì)對(duì)“索引失效”有直觀感受。實(shí)際開(kāi)發(fā)中SQL性能問(wèn)題的排查流程第一步永遠(yuǎn)是看執(zhí)行計(jì)劃和索引使用情況。4.2 訂單表設(shè)計(jì)一個(gè)典型的場(chǎng)景設(shè)計(jì)題美團(tuán)作為交易平臺(tái)訂單表設(shè)計(jì)是業(yè)務(wù)系統(tǒng)的核心。模擬題中如果出現(xiàn)“設(shè)計(jì)一個(gè)訂單表”之類(lèi)的問(wèn)題考察的不僅是建表語(yǔ)句更是你對(duì)業(yè)務(wù)的理解。我提供一個(gè)可參考的設(shè)計(jì)思路訂單主表字段包括訂單號(hào)、用戶ID、商戶ID、總金額、訂單狀態(tài)、創(chuàng)建時(shí)間、支付時(shí)間等。訂單號(hào)要全局唯一通常用分布式ID生成策略避免單庫(kù)自增主鍵的性能瓶頸。訂單明細(xì)表記錄每個(gè)商品的名稱、數(shù)量、單價(jià)、快照信息。這里的“快照”很關(guān)鍵因?yàn)樯唐访Q和價(jià)格可能隨時(shí)間變化訂單必須保存下單當(dāng)時(shí)的快照用于后續(xù)對(duì)賬和售后。索引設(shè)計(jì)高頻查詢維度通常是“按用戶查訂單”和“按商戶查訂單”因此聯(lián)合索引可以設(shè)計(jì)為(user_id, create_time)和(merchant_id, create_time)兼顧過(guò)濾和排序。分表策略當(dāng)訂單量達(dá)到億級(jí)時(shí)單表無(wú)法支撐需要按用戶ID或訂單ID進(jìn)行水平分表。2016年美團(tuán)的訂單量增長(zhǎng)非??爝@類(lèi)設(shè)計(jì)考量是真實(shí)存在的。這道題的加分點(diǎn)是主動(dòng)說(shuō)出“金額用分為單位存儲(chǔ)為整數(shù)”避免浮點(diǎn)誤差以及“邏輯刪除與物理刪除的選擇”“訂單狀態(tài)流轉(zhuǎn)如何記錄”等細(xì)節(jié)。這些內(nèi)容在筆試的大題里可能不會(huì)要求全部寫(xiě)出但你在答案中體現(xiàn)的工程經(jīng)驗(yàn)深度會(huì)影響面試官對(duì)你的判斷。4.3 高并發(fā)庫(kù)存扣減從悲觀鎖到樂(lè)觀鎖庫(kù)存扣減是電商和交易類(lèi)系統(tǒng)的經(jīng)典難題在美團(tuán)的優(yōu)惠券發(fā)放、限量搶購(gòu)、庫(kù)存商品秒殺等場(chǎng)景中都會(huì)遇到。模擬題中如果延伸出“如何避免超賣(mài)”需要你掌握兩種并發(fā)控制思路。悲觀鎖使用數(shù)據(jù)庫(kù)的SELECT ... FOR UPDATE鎖定庫(kù)存行更新完成后再釋放。這種方案邏輯簡(jiǎn)單但并發(fā)性能較差容易造成鎖等待。樂(lè)觀鎖在庫(kù)存表中增加版本號(hào)字段更新時(shí)判斷版本號(hào)是否匹配UPDATE stock SET count count - 1, version version 1 WHERE product_id ? AND version ?如果更新的影響行數(shù)為0說(shuō)明版本不匹配需要重試。這種方案在沖突不頻繁時(shí)性能較好但在高競(jìng)爭(zhēng)場(chǎng)景下重試率會(huì)顯著上升。更進(jìn)一步的方案是基于Redis的原子操作扣減庫(kù)存利用DECR命令的原子性避免并發(fā)問(wèn)題異步通過(guò)消息隊(duì)列落庫(kù)。2016年很多互聯(lián)網(wǎng)公司已經(jīng)在用類(lèi)似方案應(yīng)對(duì)高并發(fā)秒殺場(chǎng)景。筆試中能寫(xiě)到這一層就已經(jīng)超出平均水平了。5. 智力題和思路題邏輯推理比答案本身更重要5.1 經(jīng)典智力題兩根不均勻的繩子如何測(cè)出45分鐘這類(lèi)題在互聯(lián)網(wǎng)公司的筆試題里反復(fù)出現(xiàn)核心考察的是“打破常規(guī)思維的建模能力”。題目版本通常是有兩根不均勻的繩子每根從一頭點(diǎn)燃后恰好需要1小時(shí)燒完。問(wèn)如何用這兩根繩子測(cè)出45分鐘。標(biāo)準(zhǔn)解法是第一根繩子同時(shí)點(diǎn)燃兩頭第二根繩子只點(diǎn)燃一頭。第一根繩子燒完時(shí)恰好過(guò)去30分鐘。此時(shí)立刻點(diǎn)燃第二根繩子的另一頭第二根剩余部分原來(lái)的燃燒時(shí)間是30分鐘點(diǎn)燃兩頭后將在15分鐘內(nèi)燒完??偤臅r(shí)30 15 45分鐘。這類(lèi)題的得分點(diǎn)在于你能否“一邊燒繩子一邊改變?nèi)紵龡l件”本質(zhì)上是在用事件并發(fā)建模時(shí)間。面試官想看到的是你遇到新問(wèn)題時(shí)的拆解過(guò)程。如果沒(méi)見(jiàn)過(guò)這道題也別慌可以把思考步驟說(shuō)出來(lái)“先看能確定哪些基本時(shí)間量——從一頭燒是60分鐘從兩頭燒是30分鐘然后基于這個(gè)基礎(chǔ)組合推導(dǎo)?!边@種結(jié)構(gòu)化的解題過(guò)程本身就能拿到不錯(cuò)的印象分。5.2 邏輯推理題如何用兩步推理解決看似復(fù)雜的限制另一類(lèi)常見(jiàn)邏輯題是“用無(wú)刻度的桶量出固定容量的水”。比如一個(gè)5升桶和一個(gè)3升桶如何量出4升水。解法是3升桶裝滿倒入5升桶此時(shí)5升桶有3升再裝滿3升桶倒入5升桶直到滿此時(shí)3升桶剩余1升倒掉5升桶的水把3升桶中的1升倒入5升桶再裝滿3升桶倒入5升桶得到4升。這類(lèi)題背后的通用策略可以歸納為列出所有可能的“狀態(tài)”和“操作”。尋找狀態(tài)之間的轉(zhuǎn)移路徑。本質(zhì)是一個(gè)“狀態(tài)空間搜索”問(wèn)題。把這個(gè)思路說(shuō)出來(lái)比死記題目答案更有價(jià)值。因?yàn)槊嬖嚬僭诠P試之后很可能追問(wèn)“你能用程序?qū)懗鲞@個(gè)量水問(wèn)題的求解過(guò)程嗎”如果你有“狀態(tài)轉(zhuǎn)移”的意識(shí)就能聯(lián)想到用廣度優(yōu)先搜索BFS窮舉狀態(tài)空間這就是編程能力和邏輯思維的結(jié)合點(diǎn)。5.3 場(chǎng)景開(kāi)放題如果外賣(mài)訂單突然暴漲你會(huì)怎么設(shè)計(jì)系統(tǒng)開(kāi)放題沒(méi)有唯一答案但閱卷人通常期待你用“分層拆解 權(quán)衡取舍”的方式回應(yīng)。我建議的回答框架是先分層接入層、應(yīng)用層、數(shù)據(jù)層分別怎么擴(kuò)容。接入層加負(fù)載均衡節(jié)點(diǎn)應(yīng)用層無(wú)狀態(tài)化水平擴(kuò)展服務(wù)實(shí)例數(shù)據(jù)層的讀多寫(xiě)少場(chǎng)景引入緩存寫(xiě)多場(chǎng)景考慮分庫(kù)分表或消息隊(duì)列削峰。再識(shí)別瓶頸2016年的外賣(mài)訂單系統(tǒng)瓶頸往往在數(shù)據(jù)庫(kù)寫(xiě)入和外部接口調(diào)用。優(yōu)惠券、支付、商戶系統(tǒng)之間的同步調(diào)用會(huì)導(dǎo)致鏈路變長(zhǎng)。最后談取舍最終一致性與強(qiáng)一致性的選擇、緩存與數(shù)據(jù)庫(kù)的一致性維護(hù)、降級(jí)與限流的觸發(fā)條件。這道題里你能說(shuō)出幾個(gè)專業(yè)術(shù)語(yǔ)和真實(shí)場(chǎng)景就能體現(xiàn)出工程寬度。如果你還能主動(dòng)提到“訂單狀態(tài)機(jī)的流轉(zhuǎn)設(shè)計(jì)”“冪等鍵的使用”那就更出彩了。6. 備考實(shí)操?gòu)哪M題到真實(shí)筆試的完整路徑6.1 時(shí)間分配策略選擇題與編程題的比例控制真實(shí)筆試通常時(shí)間是緊張的很多候選人死在“前面選擇題斟酌太久后面編程題沒(méi)時(shí)間寫(xiě)”。我的建議是先用5分鐘快速瀏覽全部題目給編程題預(yù)留充足時(shí)間。以一套90分鐘的試卷為例如果有20道選擇題和2道編程題前10分鐘快速過(guò)一遍選擇題能確定的果斷作答不確定的先標(biāo)記。用50分鐘做編程題先審題確定數(shù)據(jù)結(jié)構(gòu)和算法模型再寫(xiě)代碼最后花幾分鐘自測(cè)邊界。最后回頭處理剛才標(biāo)記的選擇題時(shí)間剩余越少越不能糾結(jié)。“先做編程題”這個(gè)反直覺(jué)的做法我建議你務(wù)必嘗試。因?yàn)榫幊填}分值高、區(qū)分度大而選擇題即使蒙也有概率得分。把精力放在能穩(wěn)定拿分的地方是考試的基本法則。6.2 刷題的正確姿勢(shì)從題海戰(zhàn)術(shù)到專題突破不要盲目刷題??吹揭惶啄M題后先把錯(cuò)題和不確定的題分門(mén)別類(lèi)找到自己的薄弱專題。比如鏈表題總寫(xiě)不對(duì)就集中刷20道鏈表題直到三種主要反轉(zhuǎn)變體和快慢指針?biāo)悸范际炀殹K㈩}時(shí)我習(xí)慣用一個(gè)表格記錄自己的完成情況題目類(lèi)型首次正確最優(yōu)復(fù)雜度是否理解原理一周后復(fù)現(xiàn)鏈表反轉(zhuǎn)是O(n)是可復(fù)現(xiàn)動(dòng)態(tài)規(guī)劃否超時(shí)否需復(fù)習(xí)滑動(dòng)窗口是O(n)是可復(fù)現(xiàn)生產(chǎn)者消費(fèi)者否概念不清否需復(fù)習(xí)這個(gè)表格的價(jià)值在于它能幫你明確“哪些題需要重做哪些題只需要看思路”。復(fù)習(xí)時(shí)優(yōu)先處理“需復(fù)習(xí)”的題目因?yàn)樗鼈兙褪悄惴謹(jǐn)?shù)的增長(zhǎng)點(diǎn)。6.3 筆試之外的準(zhǔn)備簡(jiǎn)歷與技術(shù)棧的匹配度模擬題做得再順也只是筆試環(huán)節(jié)。2016年美團(tuán)的招聘流程筆試之后還有多輪技術(shù)面試面試的核心圍繞簡(jiǎn)歷上的項(xiàng)目和基礎(chǔ)知識(shí)展開(kāi)。所以筆試前也要同步準(zhǔn)備簡(jiǎn)歷中的技術(shù)棧描述別給自己挖坑。寫(xiě)簡(jiǎn)歷時(shí)遵循“技術(shù)棧 業(yè)務(wù)場(chǎng)景 量化結(jié)果”的格式。比如負(fù)責(zé)外賣(mài)訂單系統(tǒng)的后端開(kāi)發(fā)基于Spring Boot構(gòu)建訂單查詢接口通過(guò)優(yōu)化SQL索引使接口平均耗時(shí)從500ms降低到120ms。面試官看到這樣的描述很容易在面試中針對(duì)“索引優(yōu)化”展開(kāi)提問(wèn)而你恰好有備而來(lái)。反過(guò)來(lái)如果你只寫(xiě)“負(fù)責(zé)訂單系統(tǒng)開(kāi)發(fā)”面試官只能自己找問(wèn)題容易問(wèn)到你完全不熟悉的領(lǐng)域。如果你準(zhǔn)備的是校招崗位項(xiàng)目經(jīng)驗(yàn)不夠深沒(méi)關(guān)系但至少要把模擬題涉及的基礎(chǔ)知識(shí)體系完整過(guò)一遍?;A(chǔ)扎實(shí)的候選人即使沒(méi)有亮眼的項(xiàng)目也有很大機(jī)會(huì)通過(guò)面試。7. 復(fù)盤(pán)與提升做完一套模擬題后接下來(lái)要做什么一套模擬題做完對(duì)完答案并不意味著結(jié)束。真正的學(xué)習(xí)從復(fù)盤(pán)開(kāi)始。我的習(xí)慣做法是把所有錯(cuò)題按“知識(shí)盲區(qū)”和“粗心失誤”分類(lèi)整理。知識(shí)盲區(qū)需要系統(tǒng)補(bǔ)課粗心失誤只需要在下次筆試前提醒自己注意。對(duì)于知識(shí)盲區(qū)不要只看正確答案要找到背后的知識(shí)樹(shù)。比如操作系統(tǒng)部分出錯(cuò)就梳理出“進(jìn)程管理—內(nèi)存管理—文件系統(tǒng)—I/O系統(tǒng)”的完整大綱找一本經(jīng)典教材把對(duì)應(yīng)章節(jié)過(guò)一遍。這樣做一道題能帶動(dòng)一整塊知識(shí)體系的復(fù)習(xí)效率遠(yuǎn)高于零散刷題。對(duì)于粗心失誤比如“沒(méi)看清題目要求返回 -1 而不是返回 0”這類(lèi)問(wèn)題其實(shí)最有性價(jià)比。你把“讀題時(shí)圈出邊界條件和返回值要求”作為習(xí)慣就能避免很多無(wú)謂失分。最后我建議你把這套模擬題放進(jìn)你的復(fù)習(xí)周期里每一到兩周重做一次直到所有題都能快速給出清晰思路。到那個(gè)時(shí)候你準(zhǔn)備的不只是一套題而是一整套應(yīng)對(duì)研發(fā)崗筆試的方法論。我當(dāng)時(shí)準(zhǔn)備這類(lèi)筆試時(shí)最大的體會(huì)是“筆試考的從來(lái)不是天賦而是你是否愿意踏踏實(shí)實(shí)把基礎(chǔ)打牢。”這句話聽(tīng)起來(lái)很樸素但經(jīng)歷過(guò)真實(shí)考場(chǎng)就會(huì)明白——大部分人的失敗不是輸在難題而是輸在簡(jiǎn)單題上的粗心和對(duì)基礎(chǔ)概念的一知半解。你能把模擬題里的每一道基礎(chǔ)題都講清楚“為什么”那一張筆試通過(guò)的通知書(shū)離你就不遠(yuǎn)了。