news 2026/9/3 22:07:21

浩鲸科技校招算法笔试复盘:从数据结构到机器学习核心考点

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
浩鲸科技校招算法笔试复盘:从数据结构到机器学习核心考点

浩鲸科技2019校招算法类笔试题,是很多当年投递通信软件方向校招生的必经一关。这家公司前身是中兴软创,主做电信业务支撑系统,后来在云计算、大数据、AI方向铺得很开。所以它的算法笔试有个很明显的特点:基础题量大、覆盖范围广、编程题偏工程落地,不像互联网大厂那样钻极难的动态规划优化,但如果不扎实,很容易栽在细节上。

这篇文章不打算只给一份“答案清单”,而是把这份试卷背后真正想考的东西拆开揉碎。我会按照试卷的实际结构,把数据结构、经典算法、机器学习、深度学习、编程题五个模块逐一过一遍,每个模块挑出最典型的考点,讲清楚原理和答题思路,最后再分享一些现场考试时真正能救命的经验。不管你是正在准备校招的应届生,还是想查漏补缺的从业者,这篇都能用得上。

1. 笔试整体印象:基础题量大,覆盖范围广

1.1 浩鲸算法笔试的典型结构

浩鲸科技2019年的校招算法笔试题,整体可以分为三个部分:客观题(选择+填空)、简答题、编程题。客观题覆盖数据结构、算法分析、机器学习基础,题量在30道左右,单题分值不高但容错率低;简答题主要考察对经典算法的理解深度,比如KMP的next数组怎么求、快排为什么退化、SVM的核函数怎么选;编程题通常是两道,一道偏字符串处理,一道偏动态规划或者排序,时间控制在40分钟内比较合理。

这个结构其实是很多通信软件公司的通用套路。浩鲸的核心业务是电信BSS/OSS系统,数据量大、并发高、逻辑复杂,所以它特别看重候选人的基础功底和边界处理能力。它不指望你上来就写出一个工业级分布式算法,但你必须把排序、字符串匹配、动态规划这些基本功做到肌肉记忆级别。

1.2 这份试卷想考察的三种能力

结合试卷整体风格,我总结出三个核心考察点:

第一,对算法复杂度的敏感度。电信系统里动辄就是上亿条话单,O(n^2)和O(n log n)的差距是分钟级和秒级的差距。所以试卷里会有大量关于时间复杂度的选择题,比如堆排序建堆的复杂度、快排最坏情况的触发条件、哈希冲突的解决方案等等。

第二,对经典算法原理的深度理解。简答题里考KMP的next数组,考LRU缓存淘汰策略,考二分查找的边界条件,这些都是一旦写过源码就能答对、只背结论就会翻车的题目。我在面试中见过太多能背出“快排平均O(n log n)”但写不出partition的候选人,浩鲸的题就是专门筛这种“背题党”的。

第三,工程化的编程能力。编程题不会考那种需要灵光一现的天才题,更多是“给定一堆字符串,统计出现次数并排序”这类实际工作中天天遇到的场景。但越简单越考验细节:输入输出格式、内存占用、排序稳定性,这些才是真正拉开差距的地方。

2. 数据结构与经典算法:客观题里的失分重灾区

2.1 KMP算法与next数组:那道被反复翻牌的经典题

浩鲸这次的客观题里,KMP算法几乎必考,而且考法很直接:“对于模式串p='abacaba',求其next数组”。这道题我在考场上见过,也在后来带新人时给他们出过,因为它是检验你有没有真正理解KMP的最佳试金石。

next数组的定义(以-1为起点):next[i]表示模式串前i个字符组成的子串中,最长相同前后缀的长度,但规定next[0]=-1。也就是说,next[i]的值等于p[0..i-1]这个子串的最长公共前后缀长度。这里“前缀”不包括整个子串本身,“后缀”同理。

以p="abacaba"为例一步步来:

i子串p[0..i-1]最长相同前后缀next[i]
0--1
1"a"无(前后缀均为空)0
2"ab"0
3"aba""a"(长度1)1
4"abac"0
5"abaca""a"(长度1)1
6"abacab""ab"(长度2)2
7"abacaba""aba"(长度3)3

所以最终的next数组是[-1, 0, 0, 1, 0, 1, 2, 3]。

这个计算过程笔试时一定要手写一遍再填答案,因为很多人会在这里踩一个坑:求next[6]的时候,脑子里想着"abacab"的公共前后缀,容易顺手写成"a"长度1,但实际后缀"ab"是和前缀"ab"匹配的,长度是2。这种题考的就是细心和基本功。

如果再延伸一点,KMP匹配过程中,当主串某位置失配时,模式串移动到位移 = 已匹配字符数 - next[失配位置](按next[i]表示p[0..i-1]的最长公共前后缀长度的定义来算)。理解了这一点,笔试后的大题如果让你模拟匹配过程,也能从容应对。

2.2 排序算法:从复杂度到稳定性的全面考察

排序是这份试卷里出现频率最高的考点,没有之一。原因很简单:BSS系统里到处都要排序,按时间排话单、按金额排账单、按优先级排任务,排序算法的理解程度直接反映了程序员的基础是否扎实。

选择题会考这么几个点:

哪些排序是稳定的?冒泡、插入、归并是稳定的;选择、快排、堆排是不稳定的。注意,这里有个高频陷阱:很多人以为快排不稳定是因为“交换”,其实选择排序也交换,但选择排序之所以不稳定是因为它会把后面的元素直接换到前面,破坏了相对顺序。答题时最好把每个排序的具体执行过程在脑子里过一遍,不要只背结论。

堆排序建堆的时间复杂度是多少?答案是O(n),不是O(n log n)。这个看似简单但很多人答错。原因在于从最后一个非叶子节点开始向下调整时,越底层的节点调整次数越少,总调整次数趋近于n,而不是每个节点都调整log n次。笔试题里专门考这个,就是在筛选那些只背“堆排序是O(n log n)”的人。建堆是O(n),之后每次取出堆顶再调整是O(log n),所以整体排序复杂度是O(n log n),但单说建堆阶段是O(n)。

快排最坏情况什么时候出现?当每次partition选到的基准值都是当前区间的最大或最小值时,快排退化成O(n^2)。比如对一个已经有序的数组做快排,如果基准值固定取第一个元素,那每次划分都极度不均。优化方法是三数取中或随机选基准,这个知识点简答题也爱考。

我建议备考时自己手写一遍七种常用排序(冒泡、选择、插入、希尔、归并、快排、堆排),不用跑代码,就在纸上把每一趟的数组状态写出来,这比刷十道题都管用。排序的三种核心操作——交换、插入、归并——是后面很多算法的基础,写一遍能打通很多关联知识点。

2.3 经典算法:二分、贪心与动态规划的出题套路

客观题里还有一批经典算法题,难度不大,但覆盖面特别广,我列几个高频考点。

二分查找的边界条件是命中率最高的一题。常见的坑是死循环和越界,尤其是当区间只有两个元素时,如果mid = (left+right)/2取的是左中位数,而更新逻辑是left=mid,就会死循环。这类题没有捷径,必须把“左闭右开”和“左闭右闭”两种写法的边界条件都默写熟练。

贪心算法的典型应用,比如活动安排问题、哈夫曼编码、找零钱问题,简答题常考“为什么贪心策略在这里有效”。答这类题的关键是把贪心选择性质和最优子结构说清楚,只说“每次都选结束时间最早的活动”是不够的,还要说明为什么这样不会错过全局最优解。

动态规划的常规递推,比如最长公共子序列(LCS)、最长递增子序列(LIS)、0-1背包,要么出在选择题让你算某个dp值,要么出在编程题让你实现。这类题目我在第4部分会用一个完整案例展开,这里先提一个重要结论:动态规划不靠灵光一现,而是靠“定义状态、写状态转移方程、初始化、确定遍历顺序”四步走,任何新题都能套这个框架。

3. 机器学习与深度学习:算法岗的“分水岭”板块

3.1 机器学习基础:从LR到SVM的必背结论

浩鲸的算法岗笔试,机器学习部分占了大概三分之一的篇幅。这跟公司业务有关——电信行业的数据量太庞大了,用户画像、流失预警、精准营销都是典型的机器学习落地场景。所以这部分考得非常实务,不考推导,考结论和理解。

线性回归和逻辑回归(LR)是必考点。要清楚LR虽然名字里有“回归”,但本质是分类模型,它的输出经过sigmoid函数映射到(0,1)区间,可以解释为概率。损失函数是对数损失(交叉熵),不能用均方误差的原因在于非凸性——如果用均方误差,梯度下降很可能陷入局部最优。

SVM也是高频考点。重点掌握:支持向量是距离超平面最近的那几个样本点;核函数的本质是把低维不可分的数据映射到高维空间;常用的核函数有线性核、多项式核、RBF核(高斯核),其中RBF核是最常用的,因为它只有一个参数gamma,调节起来比较方便。简答题如果问你“核函数怎么选”,答案要分层:数据量小、特征多优先用线性核;数据非线性可分,先试RBF;如果样本量极大,RBF的计算开销会很大,这时候可以试试线性核或者改用其他模型。

决策树和集成学习是另一大块。要记住C4.5用信息增益比、CART用基尼系数,它们的共同目的是解决ID3用信息增益时偏向取值较多特征的缺陷。集成学习的两个流派要区分清楚:Bagging(如随机森林)通过有放回采样降低方差,Boosting(如XGBoost、LightGBM)通过串行训练降低偏差。XGBoost在2019年前后正是最火的时候,笔试里出现“XGBoost相比传统GBDT的改进”这类题也不奇怪,至少要答出二阶泰勒展开、正则项、列抽样这几条。

聚类算法里,K-Means几乎必考。它的步骤要能默写:随机选K个中心点、分配样本到最近中心、重新计算中心、重复直到收敛。还要知道它的局限:对初始中心敏感、K值要预先指定、对非凸簇效果差。K-Means++是常见优化方案,原理是让初始中心尽量分散。

KNN这个算法也值得提一下,它虽然简单,但却是“懒惰学习”的典型代表——训练阶段不做事,预测时才计算距离。它的三个基本要素是K值选择、距离度量、分类决策规则。笔试里如果问你“KNN的三个核心是什么”,其实就是这三样。

3.2 深度学习:从反向传播到CNN的考察重点

2019年深度学习已经很热了,浩鲸这种有AI团队的公司,笔试里一定会有深度学习基础题。但别担心,它考不到Transformer那种深度,主要停留在经典内容。

反向传播是必考题。要知道它的本质是链式法则的反复应用,即损失函数对每一层参数的偏导,通过从输出层向输入层逐层传递误差来计算。选择题可能会问你“某一层的梯度消失是什么原因”,答案是激活函数饱和区导数接近0,或者网络层数过深连乘导致梯度趋近0。这引申出一个经典问题:为什么ReLU比sigmoid在深层网络中更常用?因为ReLU在正值区间的导数为1,不会放大也不会缩小梯度,有效缓解了梯度消失。

CNN的考点很具体:卷积操作怎么计算输出尺寸、池化的作用是什么。输出尺寸公式要记牢:(输入尺寸 - 卷积核尺寸 + 2×填充) / 步长 + 1。池化的作用有三个——降维、增加平移不变性、防止过拟合。笔试里如果出一道“输入224×224×3的图像,经过5×5卷积核、步长1、无填充,输出尺寸是多少”,答案是220×220×(卷积核个数),闭着眼睛都要能算出来。

优化器这块,要分清SGD、Momentum、RMSProp、Adam各自的思路。SGD的缺点是收敛慢且容易震荡;Momentum通过累积动量来加速收敛、抑制震荡;RMSProp对每个参数自适应调整学习率;Adam结合了Momentum和RMSProp,是实践中最常用的默认选择。简答题如果问“为什么Adam用得多”,答案就是它既快又稳,对超参数不敏感。

3.3 启发式算法:粒子群、模拟退火这类“冷门”考点

这里必须提一嘴粒子群算法(PSO),因为它是浩鲸这类公司笔面试里的“惊喜题”。毕竟很多应届生都把精力耗在梯度下降上,对启发式算法了解不多,而这类算法在工程优化问题中其实很常用——比如电信网络的资源调度、参数寻优,都能用粒子群。

粒子群的核心思想是模拟鸟群觅食:每个解是一个“粒子”,有位置和速度两个属性。迭代时每个粒子根据两个最优值更新速度:一个是自己历史最优位置pbest,一个是整个群体的历史最优位置gbest。速度更新公式是v = w×v + c1×r1×(pbest - x) + c2×r2×(gbest - x),其中w是惯性权重,c1是认知系数,c2是社会系数,r1、r2是[0,1]的随机数,然后位置x = x + v。

笔试考PSO不会让你手写完整算法,最多是选择题判断“粒子群算法属于哪一类算法”,答案是群体智能优化算法,和遗传算法、蚁群算法、模拟退火算法一起归入启发式算法。或者出一道简答题问你“如何避免粒子群早熟收敛”,可以从增大惯性权重、引入变异机制、增加种群多样性等角度回答。

模拟退火算法也是类似考察方式。它的核心是以一定概率接受比当前解更差的解,从而跳出局部最优。接受概率通常用Metropolis准则:p = exp(-(ΔE)/T),其中T是温度,随迭代逐渐降低。这个公式在选择题里出现过,要记得温度越高、接受差解的概率越大。

这些启发式算法的共同特点是“不保证找到全局最优,但在合理时间内能找到足够好的解”,回答这类简答题的万能句就是这个,再配合具体算法的机制说明,得分率会高很多。

4. 编程题实战:三个典型题目的完整复盘

4.1 题目一:字符串去重并按字典序排序

这道题原题记不太清了,大意是输入一个字符串,去掉重复字符后按字典序升序输出。比如输入"cbacd",输出去重后的"abcd"。

这道题属于“送分题”级别,但却是失分重灾区。原因在于很多人会忽略题目要求的“去重后排序”,直接用set去重,但set的输出顺序是不确定的,如果没有显式排序就会出错。我的标准解法是用一个长度为256的标记数组加排序:

#include <iostream> #include <string> #include <vector> #include <algorithm> int main() { std::string s; std::cin >> s; std::vector<bool> seen(256, false); for (char c : s) { seen[(unsigned char)c] = true; } std::string result; for (int i = 0; i < 256; i++) { if (seen[i]) { result.push_back((char)i); } } std::cout << result << std::endl; return 0; }

注意这里有个小技巧:直接用ASCII码的递增顺序遍历标记数组,自然就实现了字典序排序,不需要再调用sort函数。如果输入包含中文字符,范围要调整,但在校招笔试的字符串题里,题目一般会说明“输入由小写字母组成”,这种情况直接用bool seen[26]更简洁。做题前一定先把题目约束条件看清楚,这个习惯比会写代码更重要。

4.2 题目二:最长公共子序列(LCS)

这题在2019年的笔试里出现过,而且当年很多人在状态定义上犯了错。LCS要求的是子序列,不要求连续,所以不能用滑动窗口,必须用动态规划。

状态定义:dp[i][j]表示字符串A的前i个字符和字符串B的前j个字符的最长公共子序列长度。

状态转移方程:

  • 如果A[i-1] == B[j-1],那么dp[i][j] = dp[i-1][j-1] + 1
  • 否则dp[i][j] = max(dp[i-1][j], dp[i][j-1])

初始化:dp[0][j] = 0,dp[i][0] = 0,因为空串和任何串的最长公共子序列都是0。

这里最容易出错的地方是:字符串下标从0开始,但dp下标从1开始,所以比较时要写A[i-1]和B[j-1],而不是A[i]和B[j]。我见过无数人在考场上因为这个下标偏移而Debug不出来,白白浪费时间。

用Python写更直观:

def lcs(a: str, b: str) -> int: n, m = len(a), len(b) dp = [[0] * (m + 1) for _ in range(n + 1)] for i in range(1, n + 1): for j in range(1, m + 1): if a[i - 1] == b[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return dp[n][m]

如果题目要求输出具体子序列,需要在计算dp时记录每个转移方向(左上方、上方、左方),再回溯。但笔试题的编程题通常只要求输出长度,所以优先保证核心逻辑正确即可。能写得快、写得对,比写出花活重要。

4.3 题目三:Top K问题——海量数据场景下的必考题

浩鲸的业务决定了它很爱考海量数据处理。Top K问题几乎是必考,形式是“给定n个数,找出其中最大的K个数”。

最直接的做法是排序后取前K个,复杂度O(n log n),如果K远小于n,这种做法在数据量大时会显得很蠢。更好的方案有两个:

方案一:小根堆,维护大小为K的堆。遍历数据,当堆中元素不足K个时直接插入;堆满后,如果当前元素大于堆顶,就用当前元素替换堆顶,然后向下调整。遍历结束后堆里就是最大的K个数。复杂度为O(n log K),空间O(K)。K比较小时效率远高于全排序。

用priority_queue实现最方便:

#include <iostream> #include <queue> #include <vector> std::vector<int> topK(const std::vector<int>& nums, int k) { std::priority_queue<int, std::vector<int>, std::greater<int>> pq; for (int num : nums) { if (pq.size() < k) { pq.push(num); } else if (num > pq.top()) { pq.pop(); pq.push(num); } } std::vector<int> result; while (!pq.empty()) { result.push_back(pq.top()); pq.pop(); } return result; }

这里的关键点是priority_queue默认是大根堆,要取K个最大值必须用std::greater<int>反转成小根堆,堆顶是堆中最小的元素,这样才能把更小的值踢出去。这个细节很多人在笔试时忘记,导致整个堆的维护方向反了。

方案二:快排的partition思想,即快速选择。利用partition将数组分成大于基准值和小于基准值两部分,如果大于基准值的部分长度刚好是K,直接返回;如果大于K,递归处理那一部分;如果小于K,则要把右侧元素也拿上。平均复杂度O(n),最坏O(n^2)。笔试时如果要求“时间复杂度O(n)”必须用这个方案,但实现起来边界情况多,建议现场先用小根堆方案保底,有时间再去优化。

5. 常见失分点与现场应对锦囊

5.1 失分点一:只给思路不写复杂度

这是我在批改模拟笔试时最痛心的失分点。很多候选人明明算法写得对,但忘记标注时间复杂度和空间复杂度,白白丢分。笔试题的评分标准里,复杂度的正确性占相当比例,因为面试官需要快速判断你是否具备算法优化的意识。我的习惯是每写完一段核心代码,紧跟一行注释,像// 时间复杂度O(n log K),空间复杂度O(K)。这既方便自己检查,也方便改卷人给分。

5.2 失分点二:边界条件考虑不全

边界条件是编程题扣分的最大头,常见的坑包括:空字符串、长度为1的数组、数组元素为负数、K值为0或等于数组长度、整数溢出。我在考场上的习惯是先处理异常分支再写主逻辑,把if (s.empty())if (k <= 0 || k > nums.size())这种判断放在函数最前面,然后才开始正常逻辑。这个习惯一旦养成,能帮你避开大量隐藏bug。

5.3 笔试现场的时间分配建议

以浩鲸这份试卷为例,总分100分,客观题占40分、简答题占30分、编程题占30分。建议时间分配:客观题30分钟,简答题25分钟,编程题35分钟,最后留10分钟检查。客观题和简答题不要恋战,一道题超过两分钟还没把握就先跳过,编程题的分值更重,但也不能因为一道编程题卡死而放弃后面的题。先在草稿纸上列出伪代码框架,确认逻辑正确再敲代码,比边写边想效率高得多。

注意:笔试题里如果出现“请描述解决思路”这类简答题,即使不会写完整代码,也要把“算法名称、大致步骤、时间和空间复杂度”这三要素写全,混个过程分很容易,因为改卷人最关注的就是你有没有算法思维。

5.4 考前的最后一周怎么准备

这一条是针对还没参加笔试的读者。如果你只剩一周时间,我建议不要再去啃新题,而是做三件事:一是把常见排序、KMP、二分、DP的模板代码手写三遍以上,做到肌肉记忆;二是把所有笔记整理成一张“复杂度速查表”,特别是排序稳定性和常见算法的复杂度,这是选择题的送分题;三是找两套往年真题,严格按考试时间模拟一遍,重点锻炼时间分配能力。

还有一个容易被忽视的点:浩鲸的笔试通常是在线OJ系统,输入输出格式和普通IDE不一样,如果平时在本地IDE里写惯了cin >> n,到了在线系统有可能连基本输入都搞不定。考前一定要去牛客网或力扣熟悉一下在线答题的输入输出方式,尤其是多行输入和以EOF结尾的输入处理。细节决定成败,这句话在校招笔试里永远适用。

最后再分享一个小技巧

我在准备这类通信软件公司笔试时,发现一个很高效的复习方法:把所有考点拆成一张“考点矩阵”表格,横轴是知识点(KMP、快排、DP、LR、SVM、CNN等),纵轴是考察形式(选择题、简答题、编程题),然后给每个格子标注自己的熟练度。复习时优先突破“会做选择题但写不出代码”和“能写出代码但说不清原理”这两类,因为这两种薄弱项在面试阶段一定会暴露。这张矩阵表直到今天我都还留着,工作后带实习生也让他们用同样的方法查漏补缺。

浩鲸这份笔试题的难度放在今天来看依然有参考价值,至少它让我在毕业后意识到一个事实:校招笔试不是考你懂多少高深算法,而是考你在压力下能不能写出干净、正确、有复杂度意识的代码。把这个基本功练扎实,不管去面哪家公司,都不会太慌。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/31 18:26:27

STM32 TrustZone实战:从原理到安全双工程配置

TrustZone这个词&#xff0c;做M系列的朋友最近两年应该没少听。它最早是Arm在Cortex-A上推出的硬件隔离方案&#xff0c;用来保护Android、Linux这类复杂系统里的密钥和支付数据。后来Arm把TrustZone下放到Cortex-M&#xff0c;在Armv8-M架构里重新实现了一套&#xff0c;ST把…

作者头像 李华
网站建设 2026/9/2 9:35:58

paperclipai实战:用Python打造AI文件自动整理与归档工具

之前在业务迭代中接触到一个叫paperclipai / paperclip的项目命名&#xff0c;起初以为只是某个回形针图标的开源库&#xff0c;真正动手后发现&#xff1a;paperclip这个词在软件工程里本身就承担着好几层含义&#xff0c;从文件上传组件到轻量 AI 工具&#xff0c;甚至还能演…

作者头像 李华