
1. 從投遞到筆試提前批的整體情況與備考定位1.1 網易提前批筆試到底考什么先說說整體感受。網易2023校招機器學習算法工程師的提前批筆試和正式批相比有個很明顯的區別題量不算大但覆蓋面非常廣而且時間緊。我當時做完第一感受是這不光是在考你會不會調包調參而是在考你的計算機基礎功底和算法思維底子。整個筆試分為幾個部分單選、多選、編程題外加一部分機器學習相關的理論問答。其中單選和多選涵蓋了數據結構、操作系統、計算機網絡以及機器學習基礎理論。編程題則偏向經典的算法題比如排序、字符串匹配、圖論搜索這類。這個崗位的特點是算法工程師首先得是合格的工程師。很多同學會有一個誤區覺得機器學習算法工程師筆試就應該狂考神經網絡、Transformer、損失函數推導但實際上筆試里數據結構與算法的權重非常高甚至比機器學習理論考察的比例還高。網易的筆試風格也延續了大廠算法崗的一貫思路基礎不牢地動山搖。你如果只背了一堆模型面試題但代碼能力跟不上編程題就會卡住。提前批和正式批的另一個區別是提前批的筆試通過后會直接進入面試流程面試官會拿著你的筆試成績來評估你的技術深度所以筆試表現直接決定后續面試的起評。我當時是在牛客網的系統上完成的筆試全程攝像頭監控雙機位倒計時嚴格整個氛圍還是比較緊張的。1.2 我的備考時間線與方法論我是提前大概兩周開始集中準備的。兩周時間不算長所以策略很重要。核心原則是花最少的時間拿住基礎分再花精力突破難點。具體時間分配是這樣的前三天用來過數據結構核心考點包括數組、鏈表、棧、隊列、樹、圖、堆、哈希表第四到第七天集中刷排序、搜索、動態規劃和字符串匹配的經典題第八到第十天過機器學習理論基礎重點看模型原理和損失函數推導最后三四天用來做模擬筆試完全按照考試時間、題量和難度來模擬。最后這個環節我強烈建議不要省略因為筆試考的不只是會不會還有在有限時間內能不能做出來模擬能幫你找到自己的做題節奏。關于刷題平臺主流的是LeetCode和牛客。牛客網有一個很大的優勢它上面有大量大廠歷年真題特別是網易的真題非常多可以直接去搜“網易2023校招筆試”相關的題庫來做。LeetCode則適合專項突破按標簽刷比如動態規劃就集中刷動態規劃不要今天刷一道鏈表明天刷一道貪心零散刷題的效率很低。2. 數據結構與算法筆試的基本盤2.1 排序算法看起來送分其實全是坑排序算法幾乎是每場大廠筆試都會出的題網易也不例外。但它的考察方式并不只是讓你寫一個快速排序而是通過選擇題或者代碼填空題來考察你對排序算法底層原理的掌握程度。比如常考的問題有快速排序在最壞情況下的時間復雜度是多少、什么情況下會發生、堆排序建堆的時間復雜度、歸并排序的空間復雜度、哪些排序算法是穩定的。我復習的時候習慣用一個表格把常見的排序算法整理清楚這個習慣強烈推薦給大家。排序算法平均時間復雜度最壞時間復雜度空間復雜度穩定性冒泡排序O(n2)O(n2)O(1)穩定選擇排序O(n2)O(n2)O(1)不穩定插入排序O(n2)O(n2)O(1)穩定希爾排序O(n^1.3)O(n2)O(1)不穩定歸并排序O(n log n)O(n log n)O(n)穩定快速排序O(n log n)O(n2)O(log n)不穩定堆排序O(n log n)O(n log n)O(1)不穩定如果筆試中遇到讓手寫排序算法的題我個人的經驗是優先寫快速排序的隨機化版本。它綜合表現最好平均時間復雜度是O(n log n)而且代碼量適中。但要注意如果題目明確要求穩定性那就得寫歸并排序快排是不穩定的。另外還有一個容易被忽略的細節快速排序的最壞情況是輸入已經有序或基本有序的時候因為每次partition只會把數組分成一邊為空、另一邊為n-1的極度不平衡狀態此時遞歸深度會退化為O(n)總時間復雜度是O(n2)。解決方法就是隨機選取基準元素或者采用三數取中法來選基準。我復習的時候反復寫了好幾遍堆排序因為它是最容易手寫出錯的排序。核心在于理解siftDown的過程其實代碼本身并不復雜。關鍵是理解建堆時為什么要從最后一個非葉子節點開始向上調整以及排序時為什么要把堆頂元素交換到數組末尾。// 堆排序核心代碼C void siftDown(vectorint nums, int i, int n) { while (i n) { int left 2 * i 1; int right 2 * i 2; int largest i; if (left n nums[left] nums[largest]) largest left; if (right n nums[right] nums[largest]) largest right; if (largest i) break; swap(nums[i], nums[largest]); i largest; } } void heapSort(vectorint nums) { int n nums.size(); // 建堆從最后一個非葉子節點開始向上調整 for (int i n / 2 - 1; i 0; i--) { siftDown(nums, i, n); } // 排序把堆頂最大值交換到末尾然后調整堆 for (int i n - 1; i 0; i--) { swap(nums[0], nums[i]); siftDown(nums, 0, i); } }2.2 字符串匹配KMP的前世今生KMP算法是網易筆試中出現頻率非常高的考點甚至可以說是必考。筆試中不僅會考你KMP的next數組怎么求還會給你一個模式串讓你直接填next數組的值。比如題里給了一個典型的模式串p abacaba讓你寫出它的next數組這就考得非常細了。next數組的定義不同教材略有差異。這里以常見的“next[i]表示p[0...i-1]的最長相等前后綴長度”這個定義為例來講解。對于模式串abacabanext[0] -1通常定義邊界值當i1時考察子串a最長相等前后綴長度為0所以next[1]0當i2時考察子串ab沒有相等前后綴next[2]0當i3時考察子串aba前綴a等于后綴a長度為1next[3]1當i4時考察子串abac沒有相等前后綴next[4]0當i5時考察子串abaca前綴a等于后綴a長度為1next[5]1當i6時考察子串abacab前綴ab等于后綴ab長度為2next[6]2當i7時考察子串abacaba前綴aba等于后綴aba長度為3next[7]3所以next數組是[-1, 0, 0, 1, 0, 1, 2, 3]。如果筆試中遇到next數組的填空題我建議用“前綴后綴最長匹配”這個樸素的方法來求雖然慢但不容易出錯。而在實際寫KMP匹配代碼的時候為了性能一般用優化后的nextval數組它考慮了字符相等時的特殊情況。// KMP算法核心代碼C vectorint getNext(const string p) { int n p.size(); vectorint next(n 1, 0); next[0] -1; int i 0, j -1; while (i n) { if (j -1 || p[i] p[j]) { i; j; // 優化如果p[i] p[j]則next[i] next[j] if (i n p[i] ! p[j]) next[i] j; else next[i] next[j]; } else { j next[j]; } } return next; } int kmp(const string s, const string p) { int i 0, j 0; vectorint next getNext(p); int sn s.size(), pn p.size(); while (i sn j pn) { if (j -1 || s[i] p[j]) { i; j; } else { j next[j]; } } return j pn ? i - j : -1; }這里有一個非常容易踩的坑next數組求的是模式串自身的匹配關系它的核心價值在于匹配失敗時不需要回退文本串的指針。為什么KMP能把時間復雜度優化到O(nm)因為當一次匹配失敗時它利用next數組把模式串右移跳過了那些必然不匹配的位置。如果你不理解這個“跳過”的過程寫出來的代碼很容易出錯。2.3 圖論與搜索從Dijkstra到二分圖HK算法大廠筆試的編程題里圖論算法也是常客。網易提前批雖然不一定會出特別難的圖論題但基礎的圖論算法你得熟練掌握。高頻考點包括Dijkstra求最短路、拓撲排序、并查集、以及二分圖相關的算法。Dijkstra算法是經典的單源最短路徑算法適用于邊權非負的圖。它的核心思想是貪心每次從未確定的節點中選一個距離最小的加入已確定集合然后松弛它的鄰居。樸素實現的時間復雜度是O(V2)用優先隊列優化后可以達到O((VE) log V)。筆試中如果數據量超過10^4個節點就一定要用優先隊列實現否則會超時。熱搜詞里提到了“二分圖 HK算法”這個在算法崗筆試中屬于進階考點。HK算法全稱Hopcroft-Karp算法是在匈牙利算法基礎上用BFS和DFS結合來求二分圖最大匹配。核心思路是先用BFS把匹配關系分層構建出增廣路再用DFS沿著增廣路進行匹配擴展。它的時間復雜度是O(E√V)比樸素的匈牙利算法O(VE)快很多。雖然網易筆試直接考HK算法的概率不高但二分圖匹配的基本概念還是可能出現在選擇題里的比如“二分圖的最大匹配數等于什么”“匈牙利算法的原理是什么”等等。還有一個容易被忽略但很重要的數據結構是并查集。筆試中很多看似復雜的題目比如判斷圖中有多少個連通分量、判斷兩個節點是否相連本質上都可以用并查集解決。并查集的代碼很簡短但路徑壓縮和按秩合并這兩個優化是必須掌握的。// 并查集核心代碼C class UnionFind { private: vectorint parent, rank; public: UnionFind(int n) { parent.resize(n); rank.resize(n, 0); for (int i 0; i n; i) parent[i] i; } int find(int x) { if (parent[x] ! x) parent[x] find(parent[x]); // 路徑壓縮 return parent[x]; } void unite(int x, int y) { int rx find(x), ry find(y); if (rx ry) return; if (rank[rx] rank[ry]) { parent[rx] ry; } else if (rank[rx] rank[ry]) { parent[ry] rx; } else { parent[ry] rx; rank[rx]; } } };2.4 貪心與動態規劃送分題與送命題這兩類算法題是筆試編程題的核心。貪心算法相對容易只要你能證明局部最優能推出全局最優代碼往往非常短。但難的是什么時候用貪心。我總結的經驗是如果題目滿足兩個條件——每一步選擇都不會影響后面的選擇空間、每一步都有明確的最優選擇標準——那大概率是貪心。動態規劃則是另一個極端知道是DP題但寫不出狀態轉移方程是筆試中最痛苦的事情。網易筆試動態規劃的考察范圍很廣從最基礎的背包問題、最長公共子序列到復雜的狀態壓縮DP、樹形DP都有可能出現。我的建議是短時間內優先掌握這幾類線性DP最大子數組和、最長遞增子序列、最長公共子序列區間DP石子合并、矩陣鏈乘背包DP0-1背包、完全背包樹形DP樹的最大獨立集、樹的直徑復習動態規劃的核心是理解狀態定義和轉移方程。比如最長遞增子序列樸素DP的時間復雜度是O(n2)但用貪心加二分的思路維護一個tail數組記錄長度為i的遞增子序列的最小末尾值可以優化到O(n log n)。筆試中如果數據量很大必須用優化版本。3. 機器學習理論基礎模型、損失與優化3.1 經典模型的原理考察從樸素貝葉斯到集成學習筆試選擇題中機器學習理論的考察不會像面試那樣深入讓你手推公式但基礎概念和原理必須扎實。高頻考點集中在樸素貝葉斯、邏輯回歸、SVM、決策樹、隨機森林、GBDT、XGBoost等經典模型的核心原理和適用場景。樸素貝葉斯常考的是它的“條件獨立假設”以及貝葉斯公式的應用。它假設特征之間相互獨立雖然現實數據中很難滿足這個假設但它在文本分類等場景下仍然表現不錯。做題時經常會遇到“給定先驗概率和條件概率計算某個樣本屬于哪個類”的計算題這種題一定要細心尤其是多個條件概率相乘的時候別算錯小數位。SVM的考點集中在最大間隔的思想、支持向量是什么、軟間隔與懲罰參數C的作用、核函數的作用。核函數是一個容易被混淆的知識點它本質上解決的是在高維空間計算內積的復雜度問題而不是說把數據映射到高維就一定能線性可分。常見的核函數包括線性核、多項式核、高斯核RBF核和sigmoid核高斯核是實際中最常用的因為它對應無限維映射表達能力更強但也更容易過擬合。集成學習的考點有兩個方向Bagging和Boosting的區別。Bagging的代表是隨機森林每個基學習器并行訓練用投票或平均的方式組合結果目的是降低方差Boosting的代表是AdaBoost和GBDT基學習器串行訓練每個學習器都關注前面學習器犯錯的樣本目的是降低偏差。這個區別是選擇題的常客。3.2 損失函數與優化算法理解比背公式更重要損失函數是機器學習理論筆試的另一大塊。核心損失函數包括均方誤差MSE、交叉熵損失、合頁損失、指數損失等。MSE對應的是回歸問題它有一個特點是當誤差較大時梯度也大對離群點比較敏感。所以如果數據中有明顯的異常值可以用MAE平均絕對誤差來替代它對離群點的魯棒性更好。交叉熵損失是分類問題中最常用的損失函數它的推導源于最大似然估計。對于二分類問題交叉熵損失可以寫成L -[y * log(p) (1 - y) * log(1 - p)]其中p是模型預測樣本屬于正類的概率。為什么分類問題一般不使用MSE而使用交叉熵因為MSE在結合sigmoid激活函數時由于sigmoid在兩端飽和導致梯度非常小訓練會非常慢而交叉熵與softmax結合時梯度形式是(p - y)不會出現梯度消失的問題。優化算法方面從最基礎的梯度下降到目前主流的Adam每個算法都有筆試考點。梯度下降有三種形式批量梯度下降BGD、隨機梯度下降SGD、小批量梯度下降Mini-batch GD它們的區別在于每次更新參數時用多少數據來計算梯度。解決過擬合的正則化手段L1和L2的區別也是常考點L1正則化產生稀疏解因為它等價于在參數上施加Laplace先驗L2正則化產生較小的參數但不至于為0因為它等價于施加Gaussian先驗。3.3 模型評估與調參這些細節決定成敗模型評估也是筆試中的高頻考察方向。核心考點包括準確率、精確率、召回率、F1、ROC曲線和AUC。這里有一個非常容易混淆的點精確率Precision和召回率Recall的區別。精確率是“預測為正類的樣本中真正為正類的比例”召回率是“真實為正類的樣本中被正確預測為正類的比例”。用一個簡單的例子來理解假設有100個病人其中10個人真的生病了模型預測出8個人有病但這8個人中只有6個人真的有病。那么精確率是6/875%召回率是6/1060%。當兩者出現矛盾時可以用F1分數來綜合衡量它是精確率和召回率的調和平均數。ROC曲線和AUC的考點在于理解它們的含義。ROC曲線的橫軸是假正例率FPR縱軸是真正例率TPRAUC是ROC曲線下的面積。AUC表示隨機給定一個正樣本和一個負樣本模型將正樣本排在負樣本前面的概率。AUC越接近1模型性能越好AUC0.5說明模型沒有判別能力。還有一個容易被忽略但近幾年考得越來越多的點樣本不均衡問題如何處理。常見方法包括過采樣SMOTE算法、欠采樣、修改損失函數中正負樣本的權重、使用Focal Loss等。網易筆試可能會以選擇題形式考察這些策略的基本原理。4. 手撕代碼編程題實操全過程4.1 一個完整的編程題示例與AC代碼編程題是筆試中最拉分、也最考驗綜合能力的部分。我在準備網易提前批筆試時把牛客上近三年的網易真題編程題都刷了一遍發現它的命題風格比較穩定。下面我拿一道我做過且非常典型的題來做一個完整解析。題目描述簡化版給定一個長度為n的數組nums你可以進行任意次操作每次操作選擇一個下標i將nums[i]加1或減1。求最少需要多少次操作使得數組中所有元素都相等。這道題的核心思路中位數是最優解。證明也很直觀如果所有元素都等于x那么總操作數是sum(|nums[i] - x|)這個函數是一個凸函數在x取中位數時達到最小值。如果x取在數據范圍之外總操作數只會更大。換一個角度看這道題它和“會議室安排”“求最小移動次數”是同一類問題都涉及排序和后繼元素對齊的思路。解題步驟很簡單三步走排序、找中位數、累加絕對值差。#include bits/stdc.h using namespace std; int main() { int n; cin n; vectorint nums(n); for (int i 0; i n; i) cin nums[i]; sort(nums.begin(), nums.end()); long long median nums[n / 2]; // 中位數 long long ans 0; for (int i 0; i n; i) { ans abs(nums[i] - median); } cout ans endl; return 0; }這道題雖然簡單但它考察的是你能否快速識別出“中位數最優”這個關鍵性質。筆試時時間緊張如果上來就想用DP或者二分來硬解反而容易卡殼。所以先分析問題結構、識別題型、再選擇算法這個做題順序不能亂。4.2 我在筆試現場踩過的三個坑筆試現場踩坑的代價非常高因為時間不等人。這里分享三個我親身經歷的教訓希望大家別重蹈覆轍。第一個坑是審題不仔細把輸入輸出格式搞錯了。有些題目要求輸出結果保留幾位小數有些要求用特定分隔符有些是多組輸入直到文件結尾。我筆試時有一道編程題題目要求輸出一行多個數中間用空格分隔我習慣性用了換行分隔結果整道題判斷錯誤。雖然代碼邏輯完全正確但輸出格式不對一分沒得。經驗是讀題時先用三秒鐘確認輸入輸出格式再開始寫代碼。第二個坑是編譯環境和本地環境有差異。牛客網筆試系統通常支持C14/17、Java 8/11、Python 3等環境但本地編譯器和遠程系統版本可能不同。比如C代碼里我用到了vector的某些新特性本地沒問題但線上系統用的編譯器版本較老編譯直接報錯。我的建議是筆試前提前到牛客網的模擬環境試一下自己熟悉的語言和編譯器版本寫代碼時盡量不要用太新的語言特性。第三個坑是大數溢出沒有提前預防。筆試題目給的數據范圍經常是10^9甚至10^18級別如果你用int存儲中間結果很容易溢出。我有一道題用了int存儲累加結果導致答案錯誤排查了半天才發現是溢出問題。從那以后凡是涉及累加、乘法、求和的場景我都不假思索地用long long。5. 筆試后的復盤與進階建議5.1 常見問題排查速查表根據我自己的筆試經歷把容易出錯的地方整理成一個速查表考前過一遍非常有用。問題類型具體表現解決方案整數溢出中間結果超過int范圍答案錯誤累加、乘法、求和都用long long數組越界訪問了nums[-1]或nums[n]循環條件用i n判斷邊界單獨處理KMP求錯next數組填錯匹配結果錯誤用樸素前綴后綴法驗證快排退化有序輸入時超時采用隨機化基準或三數取中遞歸超深遞歸調用層數過多棧溢出改成迭代循環寫法輸出格式錯誤分隔符、空格、換行與題目要求不符先確認輸入輸出格式再寫代碼浮點數精度保留小數位不足或過多用printf/格式化字符串控制輸出還有一個小技巧筆試時如果第一遍提交沒有AC不要慌先檢查邊界條件。比如數組長度為1時、輸入為空時、元素都相同時你的代碼能否正確處理。很多隱藏的測試用例都是在考邊界條件。5.2 關于機器學習算法工程師這個方向我的幾點體會最后聊聊筆試之外的一些想法。網易提前批筆試只是整個求職過程的第一步但它能很清晰地反映出目前在機器學習算法工程師這個崗位上的整體認知趨勢算法工程能力與機器學習理論并行數據結構基礎與模型原理缺一不可。如果你在準備過程中發現排序算法寫起來都費勁那就意味著刷題量還不夠如果你覺得損失函數推導無從下手那說明理論部分需要重新過一遍。我個人的體會是準備筆試最好的狀態不是把所有題目都刷完而是建立一套完整的知識框架確保拿到任何一道題都能快速歸類到對應的技術棧里。遇到一道編程題你要能在十秒內判斷它屬于排序、搜索、DP、圖論中的哪一類然后快速調用對應的模板。遇到一道機器學習選擇題你要能迅速定位到它考的是模型原理、損失函數、優化算法、模型評估中的哪個模塊然后根據已知的結論去匹配選項。這種快速歸類的能力沒有捷徑只能通過大量練習來形成。我當時是把所有做錯的題、踩過的坑、總結的模板都放在一個文檔里考前翻一遍。這樣做的好處是你對自己容易出錯的地方有清晰的認知上考場時心里就有底了。網易2023提前批筆試已經過去一段時間了現在回想起來那些為了弄懂一個算法而翻來覆去推導的夜晚那些看似枯燥的重復刷題最終都在考場上變成了實實在在的分數。所以如果你正在準備類似的大廠算法崗筆試別想太多靜下心來先把一道題一道題做好。機會永遠是留給準備好了的人。