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