筆試題詳解:從數(shù)組指針到Java與Linux核心考點(diǎn))
又到了一年一度的春招筆試季。身邊不少學(xué)弟學(xué)妹在刷題的時(shí)候翻到一份老題——攜程2016研發(fā)工程師筆試題跑來(lái)問(wèn)我還有沒(méi)有參考價(jià)值。我的回答是不但有而且很值得做。技術(shù)面試的考察內(nèi)核這七八年變化并不大數(shù)據(jù)結(jié)構(gòu)、算法、語(yǔ)言基礎(chǔ)、系統(tǒng)常識(shí)這幾大塊始終是互聯(lián)網(wǎng)公司研發(fā)崗的標(biāo)配。攜程作為OTA行業(yè)的老牌玩家它的筆試題非常能代表主流互聯(lián)網(wǎng)公司的出題風(fēng)格不偏不怪但處處埋坑考察的是你“有沒(méi)有真正寫過(guò)代碼”而不是“背了多少八股”。這份筆試題覆蓋了數(shù)組與指針、Java基礎(chǔ)、Linux操作、算法與數(shù)據(jù)結(jié)構(gòu)、數(shù)據(jù)庫(kù)等核心方向跟我后面幾年面試候選人時(shí)出的題目重合度很高。把這套題吃透你收獲的不僅是一份答案而是互聯(lián)網(wǎng)研發(fā)崗筆試的通用解題框架。這篇文章我就以這份題為線索把每一類考點(diǎn)的出題意圖、解題思路、易錯(cuò)點(diǎn)掰開(kāi)揉碎講一遍最后再聊聊怎么通過(guò)筆試復(fù)盤反推后續(xù)面試的準(zhǔn)備方向。無(wú)論你是即將參加校招的應(yīng)屆生還是準(zhǔn)備跳槽的初中級(jí)工程師這篇文章都值得你花半小時(shí)認(rèn)真讀一遍。1. 2016年攜程筆試的整體畫像與考察邏輯1.1 題量與題型分布先還原一下這套題的大致結(jié)構(gòu)。2016年攜程研發(fā)工程師筆試通常是線上筆試總時(shí)長(zhǎng)約90分鐘到120分鐘題型分為三類單選題、多選題、編程題。單選題大概20道左右覆蓋C/C、Java、數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)多選題5到8道專門用來(lái)篩“半懂不懂”的候選人編程題2到3道以字符串處理、鏈表操作、基礎(chǔ)算法為主要求手寫完整可運(yùn)行的代碼。這個(gè)結(jié)構(gòu)到今天依然是主流互聯(lián)網(wǎng)公司筆試的模板。單選題考察知識(shí)面的廣度多選題考察概念的準(zhǔn)確度編程題考察工程實(shí)現(xiàn)的熟練度。三者組合在一起基本能勾勒出一個(gè)候選人的技術(shù)輪廓知不知道、懂不懂、能不能寫出來(lái)三個(gè)層次一次摸清。很多人輕視選擇題覺(jué)得隨便蒙也能過(guò)。實(shí)際上攜程這類公司的選擇題并不是單純背誦題而是大量基于“運(yùn)行結(jié)果推演”的題目。給你一段代碼問(wèn)輸出什么給你一個(gè)函數(shù)聲明問(wèn)sizeof的結(jié)果是多少給你一個(gè)Linux命令問(wèn)它的作用。這些題目表面是選擇題本質(zhì)上還是編程題只是不用你寫完整代碼而已。1.2 為什么互聯(lián)網(wǎng)公司都這么考你會(huì)發(fā)現(xiàn)一個(gè)現(xiàn)象無(wú)論攜程、阿里、騰訊還是美團(tuán)筆試題的結(jié)構(gòu)驚人地相似。這不是互相抄襲而是互聯(lián)網(wǎng)公司對(duì)研發(fā)工程師的能力預(yù)期高度一致。一個(gè)合格的研發(fā)工程師既要能寫出正確高效的業(yè)務(wù)代碼也要能理解底層運(yùn)行機(jī)制還要能在Linux服務(wù)器上定位問(wèn)題。這三項(xiàng)能力對(duì)應(yīng)到筆試?yán)锞褪菙?shù)據(jù)結(jié)構(gòu)與算法、語(yǔ)言底層原理、系統(tǒng)與網(wǎng)絡(luò)知識(shí)。以攜程的業(yè)務(wù)場(chǎng)景為例。作為在線旅游平臺(tái)攜程的系統(tǒng)要處理高并發(fā)的搜索和預(yù)訂請(qǐng)求核心系統(tǒng)對(duì)性能極其敏感。所以筆試?yán)飻?shù)據(jù)結(jié)構(gòu)與算法是重頭戲尤其是鏈表、哈希表、字符串處理這些在業(yè)務(wù)代碼里天天用的東西。同時(shí)攜程的后端大量使用Java所以Java基礎(chǔ)成為必考項(xiàng)。再加上典型的互聯(lián)網(wǎng)部署環(huán)境是Linux服務(wù)器Linux命令和Shell腳本能力也順理成章進(jìn)入了考察范圍。理解了這一層你就明白刷題的重點(diǎn)應(yīng)該放在哪里。不要在冷門算法上死磕而要把高頻基礎(chǔ)考點(diǎn)做到零失誤。面試官并不指望你能在筆試現(xiàn)場(chǎng)發(fā)明一個(gè)紅黑樹(shù)他們考察的是你在壓力下能不能穩(wěn)定輸出那些“應(yīng)該會(huì)”的東西。1.3 考察維度與能力模型把這份筆試題的考點(diǎn)整理成一張能力模型表會(huì)更直觀考察維度具體知識(shí)點(diǎn)考察能力語(yǔ)言基礎(chǔ)數(shù)組與指針、Java集合、String、異常編碼基本功數(shù)據(jù)結(jié)構(gòu)鏈表、棧、隊(duì)列、二叉樹(shù)、哈希邏輯建模能力算法設(shè)計(jì)排序、二分、遍歷、遞歸問(wèn)題拆解能力系統(tǒng)知識(shí)Linux命令、進(jìn)程線程、內(nèi)存管理工程落地能力網(wǎng)絡(luò)基礎(chǔ)TCP/IP、HTTP、Socket分布式協(xié)作意識(shí)數(shù)據(jù)庫(kù)SQL編寫、索引原理、事務(wù)數(shù)據(jù)建模能力這六個(gè)維度基本構(gòu)成了互聯(lián)網(wǎng)研發(fā)工程師的能力底座。你可以對(duì)照這張表自查一下如果某一個(gè)維度有明顯短板筆試大概率會(huì)在這里丟分。我見(jiàn)過(guò)不少候選人算法題做得飛起結(jié)果死在Linux命令選擇題上非??上?。因?yàn)橄到y(tǒng)知識(shí)類題目往往是送分題只要你平時(shí)真的在服務(wù)器上折騰過(guò)基本不需要專門復(fù)習(xí)。2. 核心考點(diǎn)逐項(xiàng)拆解從真題看解題思路2.1 數(shù)組和指針C語(yǔ)言頭號(hào)考點(diǎn)網(wǎng)上搜“攜程筆試題”的時(shí)候高位熱詞里有“數(shù)組和指針筆試題”這足以說(shuō)明這個(gè)考點(diǎn)在筆試界的地位。數(shù)組和指針是C語(yǔ)言里最容易混淆、也最能考察候選人功底的知識(shí)點(diǎn)。2016年攜程的單選題里就有一道非常經(jīng)典的sizeof題目int a[] {1, 2, 3, 4, 5}; int *p a; printf(%zu %zu\n, sizeof(a), sizeof(p));答案在64位系統(tǒng)下是20 8。數(shù)組名a在sizeof運(yùn)算符里代表整個(gè)數(shù)組所以sizeof(a)是5個(gè)int的總大小20字節(jié)而指針p的大小只跟系統(tǒng)位數(shù)有關(guān)64位下固定是8字節(jié)。這道題考察的是“數(shù)組名什么時(shí)候退化為指針”這一核心概念。另一個(gè)高頻變形題是數(shù)組作為函數(shù)參數(shù)的情況void func(int arr[]) { printf(%zu\n, sizeof(arr)); }這里arr雖然是數(shù)組形式但在函數(shù)參數(shù)傳遞時(shí)已經(jīng)退化為指針?biāo)詓izeof(arr)在64位系統(tǒng)下依然是8而不是你期望的數(shù)組總字節(jié)數(shù)。這個(gè)坑在真實(shí)工程里也經(jīng)常踩到比如你寫一個(gè)函數(shù)想順便算出數(shù)組長(zhǎng)度結(jié)果發(fā)現(xiàn)sizeof(arr)/sizeof(arr[0])算出來(lái)是1那就是因?yàn)閿?shù)組退化成指針了。指針自增運(yùn)算也是選擇題里的??汀?p和(*p)的區(qū)別必須刻在腦子里前者先取p指向的值然后p后移后者把p指向的值加1指針本身不動(dòng)。再加上p 1和p的區(qū)別——前者不改變p后者改變p——一套組合拳下來(lái)基本功不扎實(shí)的人基本就暈了。我的建議是復(fù)習(xí)數(shù)組和指針時(shí)不要只看理論親手寫幾個(gè)小demo跑一遍。我在面試別人時(shí)經(jīng)常發(fā)現(xiàn)很多候選人能背出“數(shù)組名是常量指針”這種結(jié)論但問(wèn)他a[2]和2[a]是不是等價(jià)就答不上來(lái)了。這兩者完全等價(jià)因?yàn)閍[2]在編譯器看來(lái)就是*(a2)加法滿足交換律*(2a)自然就是2[a]。這種題目不是考語(yǔ)法冷知識(shí)而是考你是否真的理解下標(biāo)運(yùn)算的本質(zhì)。2.2 Java基礎(chǔ)攜程后端的重頭戲熱詞里“java筆試題”、“java筆試題大全帶答案”出現(xiàn)在高位說(shuō)明Java是當(dāng)前求職市場(chǎng)的主流語(yǔ)言攜程也不例外。2016年的Java題目集中在這么幾塊String相關(guān)、集合框架、異常處理、JVM基礎(chǔ)。String是Java筆試的永恒話題。有一道經(jīng)典題目是判斷下面代碼創(chuàng)建了幾個(gè)對(duì)象String s1 hello; String s2 new String(hello);答案是可能創(chuàng)建1個(gè)或2個(gè)對(duì)象。如果常量池里已經(jīng)有hello這個(gè)字面量那么new String只會(huì)在堆上創(chuàng)建一個(gè)新對(duì)象即總共1個(gè)新對(duì)象如果常量池里還沒(méi)有那么會(huì)先在常量池創(chuàng)建字面量對(duì)象再在堆上創(chuàng)建new出來(lái)的對(duì)象總共2個(gè)。而s1 s2的結(jié)果永遠(yuǎn)是false因?yàn)橐粋€(gè)指向常量池、一個(gè)指向堆。還有String、StringBuilder、StringBuffer三者的區(qū)別。String是不可變的每次拼接都會(huì)生成新對(duì)象StringBuffer是線程安全的方法加了synchronizedStringBuilder是線程不安全的但性能最高。在單線程環(huán)境下手動(dòng)拼接字符串首選StringBuilder。集合框架里HashMap是必考中的必考。Jdk 1.8之后的HashMap底層是數(shù)組加鏈表加紅黑樹(shù)當(dāng)鏈表長(zhǎng)度超過(guò)閾值8且數(shù)組長(zhǎng)度大于等于64時(shí)鏈表會(huì)轉(zhuǎn)成紅黑樹(shù)以減少查詢時(shí)間。默認(rèn)初始容量16負(fù)載因子0.75擴(kuò)容時(shí)按2倍進(jìn)行。這些數(shù)字在面試中經(jīng)常被追問(wèn)最好能說(shuō)出原理而不只是背參數(shù)負(fù)載因子0.75是空間和時(shí)間的一個(gè)折中太小了導(dǎo)致頻繁擴(kuò)容浪費(fèi)空間太大了導(dǎo)致哈希沖突增加影響查詢效率。HashMap為什么線程不安全也值得深入理解。并發(fā)put時(shí)可能發(fā)生數(shù)據(jù)覆蓋擴(kuò)容時(shí)在1.7及之前可能形成環(huán)形鏈表導(dǎo)致死循環(huán)1.8優(yōu)化了頭插法改尾插法但數(shù)據(jù)覆蓋問(wèn)題依然存在。所以并發(fā)場(chǎng)景要用ConcurrentHashMap它通過(guò)CAS加synchronized保證線程安全而不是像Hashtable那樣粗暴地鎖整個(gè)方法。我建議把HashMap的put和get流程自己畫一遍能畫清楚基本就能應(yīng)對(duì)80%的Java集合面試題。2.3 Linux與系統(tǒng)白給的送分題只要你在Linux服務(wù)器上部署過(guò)應(yīng)用、看過(guò)日志Linux相關(guān)的選擇題基本就是送分題。但很多只在學(xué)校里用過(guò)Windows的同學(xué)會(huì)在這一塊大量丟分。2016年攜程筆試?yán)镉幸坏李}問(wèn)查看端口8080被哪個(gè)進(jìn)程占用應(yīng)該用什么命令。答案組合里有netstat -tlnp | grep 8080和lsof -i:8080而這些恰恰是線上排查問(wèn)題最常用的命令。Linux命令考察方向總結(jié)起來(lái)就幾類文件操作ls、cp、mv、rm、find、tar文本處理grep、awk、sed、sort、uniq、wc進(jìn)程管理ps、top、kill、jobs、fg、bg網(wǎng)絡(luò)排查netstat、ping、telnet、curl、traceroute權(quán)限管理chmod、chown、useradd磁盤與內(nèi)存df、du、free文本處理三兄弟grep、awk、sed是面試中的高頻考點(diǎn)。舉一個(gè)實(shí)際場(chǎng)景線上日志文件access.log每行是“IP 時(shí)間 URL 狀態(tài)碼 耗時(shí)”想統(tǒng)計(jì)訪問(wèn)量最多的前10個(gè)IP命令是awk {print $1} access.log | sort | uniq -c | sort -k1 -nr | head -10這道命令組合在我面試過(guò)的公司里出現(xiàn)過(guò)無(wú)數(shù)遍。awk取出第一列IPsort排序讓相同IP相鄰uniq -c統(tǒng)計(jì)次數(shù)sort -k1 -nr按次數(shù)降序最后head -10取前10。每一步都有明確目的組合起來(lái)就是一條非常實(shí)用的分析命令。類似的還有找出日志里耗時(shí)超過(guò)500ms的請(qǐng)求awk $NF 500 {print $0} access.log$NF代表最后一列也就是耗時(shí)字段。軟鏈接和硬鏈接的區(qū)別也是??碱}。軟鏈接相當(dāng)于Windows的快捷方式可以跨文件系統(tǒng)指向源文件的路徑源文件刪除后軟鏈接失效硬鏈接是同一個(gè)inode的多個(gè)目錄項(xiàng)不能跨文件系統(tǒng)刪除源文件后硬鏈接依然能訪問(wèn)文件內(nèi)容。用ln -s創(chuàng)建軟鏈接用ln創(chuàng)建硬鏈接。對(duì)于時(shí)間緊張的復(fù)習(xí)者我的建議是把這些命令的常用參數(shù)過(guò)一遍然后在自己的電腦上裝個(gè)虛擬機(jī)或者用云服務(wù)器實(shí)際操作一遍。只看不練是記不住的敲過(guò)一遍之后肌肉記憶會(huì)幫你答題。2.4 算法與數(shù)據(jù)結(jié)構(gòu)筆試的硬骨頭算法題是筆試?yán)锓种底罡叩牟糠忠彩亲铍y臨時(shí)抱佛腳的。但好消息是互聯(lián)網(wǎng)公司筆試的算法題有一個(gè)固定的“高頻題庫(kù)”翻來(lái)覆去就是那些經(jīng)典題目。2016年攜程的編程題里有一道反轉(zhuǎn)鏈表非常典型。迭代法反轉(zhuǎn)單鏈表是必須寫在肌肉記憶里的代碼struct ListNode* reverseList(struct ListNode* head) { struct ListNode *prev NULL; struct ListNode *curr head; while (curr ! NULL) { struct ListNode *next curr-next; curr-next prev; prev curr; curr next; } return prev; }很多人第一次寫這段代碼時(shí)容易丟掉next指針的保存導(dǎo)致在移動(dòng)curr之后丟失了后續(xù)節(jié)點(diǎn)。記住一個(gè)口訣先保存next再反轉(zhuǎn)指針最后移動(dòng)prev和curr。這段代碼不到十行但包含了對(duì)鏈表指針操作的完整理解。我還建議把遞歸版本也寫一遍雖然迭代版本更高效但遞歸版本能體現(xiàn)你對(duì)問(wèn)題本質(zhì)的理解有些面試官會(huì)追問(wèn)。判斷鏈表是否有環(huán)是另一個(gè)必背題目解法是快慢指針??熘羔樏看巫邇刹铰羔樏看巫咭徊饺绻湵碛协h(huán)兩者必然相遇如果沒(méi)有環(huán)快指針會(huì)先到達(dá)末尾。這個(gè)算法的時(shí)間復(fù)雜度O(n)空間復(fù)雜度O(1)是“用空間換時(shí)間”思路的反面——用“時(shí)間換空間”的典型代表。字符串相關(guān)的題目也值得重點(diǎn)準(zhǔn)備。反轉(zhuǎn)字符串、判斷回文、統(tǒng)計(jì)字符頻率、字符串去重這些都是筆試高頻題。有一個(gè)常見(jiàn)變形是“判斷兩個(gè)字符串是否互為字符重排”解法是先統(tǒng)計(jì)每個(gè)字符出現(xiàn)次數(shù)再比對(duì)各字符次數(shù)是否一致。用數(shù)組而不是HashMap作為計(jì)數(shù)容器因?yàn)樽址秶邢迶?shù)組更高效。很多候選人一上來(lái)就new一個(gè)HashMap功能沒(méi)錯(cuò)但不夠優(yōu)雅在筆試這種資源受限環(huán)境下數(shù)組才是更好的選擇。排序算法里快排是重中之重。不僅能手寫快排還要能分析它的時(shí)間復(fù)雜度和空間復(fù)雜度??炫牌骄鶗r(shí)間復(fù)雜度O(n log n)最壞O(n^2)最壞情況是每次選取的基準(zhǔn)值都是最大值或最小值??臻g復(fù)雜度是O(log n)因?yàn)檫f歸棧的深度是log n。為了避免最壞情況可以采用隨機(jī)選取基準(zhǔn)值的方式優(yōu)化。我在筆試時(shí)會(huì)優(yōu)先寫快排因?yàn)樗嘏判?、常?shù)項(xiàng)小是實(shí)際工程中最常用的排序算法之一。二分查找也是必考但很多人寫不對(duì)邊界條件。標(biāo)準(zhǔn)模板如下int binarySearch(int nums[], int n, int target) { int left 0, right n - 1; while (left right) { int mid left (right - left) / 2; if (nums[mid] target) { return mid; } else if (nums[mid] target) { left mid 1; } else { right mid - 1; } } return -1; }幾個(gè)關(guān)鍵點(diǎn)mid計(jì)算用left (right - left) / 2而不是(left right) / 2避免溢出循環(huán)條件是left right保證區(qū)間不為空更新邊界時(shí)left mid 1和right mid - 1因?yàn)閙id已經(jīng)比較過(guò)了。如果你想找左邊界或右邊界循環(huán)條件和邊界更新邏輯又會(huì)不同。二分查找的邊界處理是面試?yán)飬^(qū)分“背過(guò)答案”和“真正理解”的試金石。3. 實(shí)戰(zhàn)推演一份模擬答卷的踩坑記錄3.1 時(shí)間分配策略我拿這套題做過(guò)一次模擬測(cè)試非常能說(shuō)明問(wèn)題選擇題糾結(jié)太久導(dǎo)致最后編程題時(shí)間不夠。很多人的筆試策略是“從頭做到尾”但這是效率最低的做法。我的建議是先把三道編程題全部讀完挑一道最有把握的先做把基本分拿到再回頭做選擇題。如果選擇題卡殼超過(guò)兩分鐘先標(biāo)記跳過(guò)去最后有時(shí)間再回來(lái)想。編程題的分?jǐn)?shù)占比往往比選擇題高得多而且選擇題可以蒙編程題不會(huì)就是不會(huì)。先把編程題做出來(lái)能有效穩(wěn)定心態(tài)后面做選擇題時(shí)也不會(huì)慌。我在模擬時(shí)就是因?yàn)樵趲椎繡語(yǔ)言題上反復(fù)糾結(jié)最后留了不到20分鐘寫編程題雖然代碼邏輯想清楚了但時(shí)間倉(cāng)促?zèng)]有仔細(xì)檢查邊界條件丟了一些不該丟的分。合理的分配是編程題占40到50分鐘選擇題和多選題占50到60分鐘最后留5分鐘檢查一遍。如果你的編程能力比較強(qiáng)可以在編程題上多花一點(diǎn)時(shí)間追求一次性寫出完美代碼如果編程能力弱一些那就保證會(huì)把的題做對(duì)不會(huì)的題寫上基本思路哪怕是偽代碼也比留空白要好。3.2 邊界條件最容易丟分的地方我閱卷的時(shí)候發(fā)現(xiàn)一個(gè)規(guī)律交給我的代碼第一眼看的不是算法思路而是邊界條件處理。如果連循環(huán)退出的邊界都處理不對(duì)這道題基本不可能通過(guò)測(cè)試用例。比如反轉(zhuǎn)鏈表如果輸入是空鏈表或只有一個(gè)節(jié)點(diǎn)直接返回head即可不用進(jìn)入循環(huán)。這個(gè)簡(jiǎn)單判斷很多人都會(huì)漏掉。二分查找的邊界是另一個(gè)重災(zāi)區(qū)。如果目標(biāo)值比數(shù)組第一個(gè)元素還小或者比最后一個(gè)元素還大應(yīng)該返回-1但很多人寫的時(shí)候循環(huán)提前退出后返回的位置是錯(cuò)的。凡是涉及數(shù)組下標(biāo)的操作都要問(wèn)自己三個(gè)問(wèn)題下標(biāo)從0開(kāi)始還是從1開(kāi)始循環(huán)條件是還是更新邊界時(shí)mid要不要加1或減1字符串相關(guān)題目還有一個(gè)常見(jiàn)的坑C語(yǔ)言字符串以\0結(jié)尾用strlen求長(zhǎng)度時(shí)不包括結(jié)尾的\0但分配內(nèi)存時(shí)要多分配一個(gè)字節(jié)。很多人用char *s malloc(strlen(str))結(jié)果越界寫入程序崩潰。這個(gè)細(xì)節(jié)在筆試中不一定能暴露因?yàn)榫€上判題系統(tǒng)可能沒(méi)有開(kāi)內(nèi)存檢測(cè)但在面試追問(wèn)中容易被發(fā)現(xiàn)。我強(qiáng)烈建議在寫完代碼后手動(dòng)跑一遍空輸入、單元素輸入、極端大輸入三個(gè)用例?;ㄥX買時(shí)間這個(gè)習(xí)慣能幫你挽回至少20%的失分。3.3 代碼風(fēng)格與可讀性筆試閱卷雖然主要靠測(cè)試用例判斷對(duì)錯(cuò)但部分公司會(huì)人工查看代碼特別是編程題的代碼。代碼風(fēng)格好不好、變量命名是否清晰、是否有注釋都會(huì)影響面試官對(duì)你的印象分。我在面試時(shí)見(jiàn)過(guò)很多候選人代碼功能完全正確但變量名清一色是a、b、c循環(huán)里塞了五層嵌套完全無(wú)法閱讀。這種人即使筆試過(guò)了面試也會(huì)很吃虧。好的筆試代碼風(fēng)格應(yīng)該是變量名有意義node、target、count函數(shù)名體現(xiàn)功能reverseList、countChars核心邏輯有注釋說(shuō)明思路。不用追求花哨的寫法但務(wù)必讓讀代碼的人一眼看明白你的思路。還有一點(diǎn)縮進(jìn)和括號(hào)風(fēng)格保持一致。有些人的代碼前面用空格后面用Tab粘貼到在線編輯器里直接亂掉這種低級(jí)錯(cuò)誤非常影響觀感。我自己的習(xí)慣是寫完代碼后大聲讀一遍。如果某個(gè)變量名讀起來(lái)不知所云改如果某個(gè)函數(shù)的長(zhǎng)度超過(guò)30行考慮拆分如果某個(gè)注釋解釋了“做什么”但沒(méi)有解釋“為什么”補(bǔ)上。這些習(xí)慣在筆試場(chǎng)景中看似浪費(fèi)了時(shí)間但對(duì)通過(guò)率和后續(xù)面試都有長(zhǎng)遠(yuǎn)價(jià)值。4. 從筆試題反推后續(xù)面試怎么準(zhǔn)備4.1 筆試題背后的技術(shù)棧線索很多人忽略了一件事筆試題本身就是技術(shù)棧的說(shuō)明書。攜程2016年這套題里Java相關(guān)占比明顯高于C語(yǔ)言說(shuō)明這家公司的主語(yǔ)言是Java。Linux命令考察占了系統(tǒng)知識(shí)的大頭說(shuō)明他們的線上環(huán)境是Linux。數(shù)據(jù)庫(kù)考了SQL語(yǔ)句和索引原理說(shuō)明業(yè)務(wù)對(duì)數(shù)據(jù)一致性要求高。這些信息都直接指向后續(xù)面試的考察方向。如果你投的是一家技術(shù)棧以Golang為主的公司筆試?yán)锎蟾怕蕰?huì)出現(xiàn)goroutine和channel相關(guān)的內(nèi)容如果是前端崗位JavaScript和瀏覽器渲染原理就會(huì)成為重頭。所以筆試不只是被篩選的過(guò)程也是你反推公司技術(shù)傾向、針對(duì)性準(zhǔn)備面試的最佳情報(bào)來(lái)源。做完筆試題后我建議你做一次復(fù)盤把每一道題對(duì)應(yīng)的知識(shí)點(diǎn)列出來(lái)標(biāo)注自己哪些題沒(méi)有把握然后逐一翻書補(bǔ)漏。這個(gè)過(guò)程比多做三套新題更有價(jià)值因?yàn)樾骂}雖然覆蓋面廣但它不能告訴你“你哪里不會(huì)”——只有自己的錯(cuò)題能。4.2 項(xiàng)目經(jīng)歷與筆試知識(shí)點(diǎn)的銜接面試和筆試是兩種完全不同的考察方式。筆試考察你有沒(méi)有知識(shí)面試考察你能不能把知識(shí)用起來(lái)。所以筆試結(jié)束后你要做的不是繼續(xù)刷題而是把筆試涉及的知識(shí)點(diǎn)和你自己的項(xiàng)目經(jīng)歷掛鉤準(zhǔn)備“筆試知識(shí)點(diǎn)項(xiàng)目場(chǎng)景面試案例”的故事。舉個(gè)例子筆試考了HashMap你的項(xiàng)目里用過(guò)緩存那就要準(zhǔn)備一個(gè)“為什么用HashMap而不用Hashtable”的實(shí)戰(zhàn)案例。筆試考了Linux的grep命令你的項(xiàng)目里做過(guò)日志分析那就準(zhǔn)備一個(gè)“用grep/awk統(tǒng)計(jì)線上異常日志”的完整故事。筆試考了數(shù)據(jù)庫(kù)索引你的項(xiàng)目里有慢查詢優(yōu)化那就要把索引失效的排查過(guò)程梳理清楚。面試官最喜歡問(wèn)的不是“HashMap的原理是什么”而是“你的項(xiàng)目里哪里用到了HashMap為什么這么用有沒(méi)有考慮過(guò)并發(fā)問(wèn)題”。如果你能把自己的項(xiàng)目經(jīng)歷和筆試知識(shí)點(diǎn)編織成故事比單純背答案要打動(dòng)人得多。這里面的核心邏輯是筆試證明你“知道”面試考察你“做過(guò)”兩者之間需要一座橋項(xiàng)目經(jīng)歷就是這座橋。4.3 給不同基礎(chǔ)讀者的準(zhǔn)備清單如果你是基礎(chǔ)比較薄弱的應(yīng)屆生我建議你把復(fù)習(xí)重心放在“高頻基礎(chǔ)考點(diǎn)”上而不是一味刷難題。先保證選擇題的正確率因?yàn)檫@部分可以通過(guò)短期記憶快速提分。數(shù)組與指針的sizeof題、Java的String/StringBuilder/StringBuffer區(qū)別、HashMap底層原理、Linux常用命令、SQL基礎(chǔ)語(yǔ)法這五塊內(nèi)容是性價(jià)比最高的復(fù)習(xí)區(qū)域投入兩三天就能見(jiàn)到明顯效果。算法題則從鏈表反轉(zhuǎn)、二叉樹(shù)遍歷、二分查找、快排這“四大金剛”開(kāi)始每個(gè)都寫出迭代和遞歸兩個(gè)版本做到閉著眼睛能寫。如果你是有兩三年經(jīng)驗(yàn)的候選人準(zhǔn)備跳槽的話筆試的壓力相對(duì)小一些但要注意“思維定式”的問(wèn)題。老手容易在HashMap的底層實(shí)現(xiàn)、Java版本特性這些新知識(shí)點(diǎn)上翻車。工作三年以上的人很多還停留在Jdk 1.7的時(shí)代而面試考的是1.8之后的特性。建議把Jdk 1.8到最新的發(fā)布說(shuō)明過(guò)一遍重點(diǎn)看語(yǔ)言特性和集合類變化這些東西在很多公司里已經(jīng)在生產(chǎn)環(huán)境用了好幾年了。還有一個(gè)通用的準(zhǔn)備策略是建立錯(cuò)題本。不是摘抄題目和答案而是記錄“我為什么會(huì)錯(cuò)”。是概念模糊是粗心大意還是時(shí)間不夠?qū)τ诟拍钅:念}目寫清楚正確的理解對(duì)于粗心大意的題目總結(jié)出提醒自己的關(guān)鍵詞。比如我自己會(huì)在錯(cuò)題本上寫“看到sizeof先看對(duì)象是數(shù)組還是指針”“看到for循環(huán)先想想空集合”“看到遞歸先想終止條件”。這些提示語(yǔ)在筆試考場(chǎng)上能幫你避免大量低級(jí)失誤。5. 資源選擇刷題時(shí)手邊該有哪幾樣?xùn)|西市面上的刷題資料多到看不過(guò)來(lái)但真正用得上的就那幾樣。我在準(zhǔn)備筆試時(shí)手邊常放三樣?xùn)|西一本算法書、一份語(yǔ)言基礎(chǔ)手冊(cè)、一個(gè)在線判題平臺(tái)的賬號(hào)。書用來(lái)看思路手冊(cè)用來(lái)查概念在線平臺(tái)用來(lái)練手。三者缺一不可只刷題不看書會(huì)導(dǎo)致知識(shí)碎片化只看書不刷題會(huì)導(dǎo)致筆試時(shí)手生寫不出代碼。在線刷題平臺(tái)選擇很多LeetCode、牛客網(wǎng)、AcWing各有各的優(yōu)勢(shì)。如果你想針對(duì)互聯(lián)網(wǎng)公司筆試我建議優(yōu)先用??途W(wǎng)因?yàn)樗念}庫(kù)里包含大量公司真題題型和真實(shí)筆試幾乎一致。LeetCode更適合系統(tǒng)性刷算法題按標(biāo)簽分類做專項(xiàng)訓(xùn)練比如這周只刷鏈表題下周只刷動(dòng)態(tài)規(guī)劃。如果你時(shí)間充裕先把LeetCode上高頻題刷一遍再去??途W(wǎng)做公司真題模擬效果最好。語(yǔ)言基礎(chǔ)手冊(cè)方面C語(yǔ)言看《C程序設(shè)計(jì)語(yǔ)言》KR就夠了Java的話《Java核心技術(shù)》配合Java官方文檔足夠應(yīng)對(duì)筆試。我不建議在復(fù)習(xí)階段看大部頭的源碼解讀類書籍那些更適合System Design面試的進(jìn)階準(zhǔn)備。筆試階段的目標(biāo)是“會(huì)做”不是“精通”不要本末倒置。計(jì)算基礎(chǔ)薄弱的讀者我建議從數(shù)據(jù)結(jié)構(gòu)可視化網(wǎng)站開(kāi)始復(fù)習(xí)。網(wǎng)站會(huì)動(dòng)態(tài)展示鏈表反轉(zhuǎn)、二叉樹(shù)遍歷、快排排序的過(guò)程畫面對(duì)比純文字更容易建立直覺(jué)。我看過(guò)可視化之后再看代碼很多“為什么這么寫”的問(wèn)題就迎刃而解了。最后再分享一個(gè)我自己用著很順的復(fù)盤方法。每做完一套題不管在線判題系統(tǒng)給不給分我都會(huì)把所有的錯(cuò)題按“概念型”“實(shí)現(xiàn)型”“邊界型”分類。概念型錯(cuò)題說(shuō)明某個(gè)知識(shí)點(diǎn)沒(méi)吃透回書本找答案實(shí)現(xiàn)型錯(cuò)題說(shuō)明代碼寫得少去在線平臺(tái)加練十道同類題邊界型錯(cuò)題說(shuō)明做題習(xí)慣不好下次寫完代碼一定手動(dòng)跑三個(gè)測(cè)試用例。這樣一輪下來(lái)每套題都能榨出最大的價(jià)值。筆試是求職路上的一道門檻但它考察的不是天賦而是你愿不愿意花時(shí)間把基礎(chǔ)打磨扎實(shí)。數(shù)組和指針、Java集合、Linux命令、算法模板這些內(nèi)容沒(méi)有一樣是“高精尖”但它們組合在一起就是一個(gè)研發(fā)工程師日常工作的核心底座。把這份2016年的老題吃透你會(huì)發(fā)現(xiàn)面試官問(wèn)來(lái)問(wèn)去還是那些東西無(wú)非換了一身馬甲而已。