
1. 从投递到笔试提前批的整体情况与备考定位1.1 网易提前批笔试到底考什么先说说整体感受。网易2023校招机器学习算法工程师的提前批笔试和正式批相比有个很明显的区别题量不算大但覆盖面非常广而且时间紧。我当时做完第一感受是这不光是在考你会不会调包调参而是在考你的计算机基础功底和算法思维底子。整个笔试分为几个部分单选、多选、编程题外加一部分机器学习相关的理论问答。其中单选和多选涵盖了数据结构、操作系统、计算机网络以及机器学习基础理论。编程题则偏向经典的算法题比如排序、字符串匹配、图论搜索这类。这个岗位的特点是算法工程师首先得是合格的工程师。很多同学会有一个误区觉得机器学习算法工程师笔试就应该狂考神经网络、Transformer、损失函数推导但实际上笔试里数据结构与算法的权重非常高甚至比机器学习理论考察的比例还高。网易的笔试风格也延续了大厂算法岗的一贯思路基础不牢地动山摇。你如果只背了一堆模型面试题但代码能力跟不上编程题就会卡住。提前批和正式批的另一个区别是提前批的笔试通过后会直接进入面试流程面试官会拿着你的笔试成绩来评估你的技术深度所以笔试表现直接决定后续面试的起评。我当时是在牛客网的系统上完成的笔试全程摄像头监控双机位倒计时严格整个氛围还是比较紧张的。1.2 我的备考时间线与方法论我是提前大概两周开始集中准备的。两周时间不算长所以策略很重要。核心原则是花最少的时间拿住基础分再花精力突破难点。具体时间分配是这样的前三天用来过数据结构核心考点包括数组、链表、栈、队列、树、图、堆、哈希表第四到第七天集中刷排序、搜索、动态规划和字符串匹配的经典题第八到第十天过机器学习理论基础重点看模型原理和损失函数推导最后三四天用来做模拟笔试完全按照考试时间、题量和难度来模拟。最后这个环节我强烈建议不要省略因为笔试考的不只是会不会还有在有限时间内能不能做出来模拟能帮你找到自己的做题节奏。关于刷题平台主流的是LeetCode和牛客。牛客网有一个很大的优势它上面有大量大厂历年真题特别是网易的真题非常多可以直接去搜“网易2023校招笔试”相关的题库来做。LeetCode则适合专项突破按标签刷比如动态规划就集中刷动态规划不要今天刷一道链表明天刷一道贪心零散刷题的效率很低。2. 数据结构与算法笔试的基本盘2.1 排序算法看起来送分其实全是坑排序算法几乎是每场大厂笔试都会出的题网易也不例外。但它的考察方式并不只是让你写一个快速排序而是通过选择题或者代码填空题来考察你对排序算法底层原理的掌握程度。比如常考的问题有快速排序在最坏情况下的时间复杂度是多少、什么情况下会发生、堆排序建堆的时间复杂度、归并排序的空间复杂度、哪些排序算法是稳定的。我复习的时候习惯用一个表格把常见的排序算法整理清楚这个习惯强烈推荐给大家。排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n²)O(n²)O(1)稳定选择排序O(n²)O(n²)O(1)不稳定插入排序O(n²)O(n²)O(1)稳定希尔排序O(n^1.3)O(n²)O(1)不稳定归并排序O(n log n)O(n log n)O(n)稳定快速排序O(n log n)O(n²)O(log n)不稳定堆排序O(n log n)O(n log n)O(1)不稳定如果笔试中遇到让手写排序算法的题我个人的经验是优先写快速排序的随机化版本。它综合表现最好平均时间复杂度是O(n log n)而且代码量适中。但要注意如果题目明确要求稳定性那就得写归并排序快排是不稳定的。另外还有一个容易被忽略的细节快速排序的最坏情况是输入已经有序或基本有序的时候因为每次partition只会把数组分成一边为空、另一边为n-1的极度不平衡状态此时递归深度会退化为O(n)总时间复杂度是O(n²)。解决方法就是随机选取基准元素或者采用三数取中法来选基准。我复习的时候反复写了好几遍堆排序因为它是最容易手写出错的排序。核心在于理解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(V²)用优先队列优化后可以达到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(n²)但用贪心加二分的思路维护一个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提前批笔试已经过去一段时间了现在回想起来那些为了弄懂一个算法而翻来覆去推导的夜晚那些看似枯燥的重复刷题最终都在考场上变成了实实在在的分数。所以如果你正在准备类似的大厂算法岗笔试别想太多静下心来先把一道题一道题做好。机会永远是留给准备好了的人。