小红书2020校招算法笔试题解析:从KMP到Dijkstra的考点与实战复盘
2026/8/31 6:49:35 网站建设 项目流程

小红书2020校招算法笔试题卷一,算是当年那批互联网校招卷子里比较有代表性的一套。我当时刷完最深的感受是:它没有刻意追求偏题怪题,而是在基础算法、数据结构、机器学习理论之间找了一个相对平衡的点。无论你投的是算法岗还是推荐算法岗,这套卷子覆盖的考点都值得认真过一遍。今天这篇文章,我以参与者和复盘者的视角,把卷子里最核心的知识点拆开讲一讲,涉及 KMP、排序、贪心、Dijkstra、KNN、聚类这些高频考点,同时把我在刷题和真实笔试中踩过的坑一并写出来,希望能帮你少走弯路。

1. 试卷整体盘点:一张卷子里的算法全景图

1.1 这份卷子考了什么:四类题型的真实分布

小红书2020校招算法笔试题卷一,整体题型可以分成四类:客观选择题、代码编程题、机器学习与深度学习基础题,以及少量偏应用的分析题。选择题主要考察数据结构、排序算法复杂度、字符串匹配等基础概念,编程题则是典型的算法实现,包括贪心、堆、图论最短路和快速幂这类高频考点。机器学习部分集中在 KNN、聚类、损失函数、过拟合处理这些面试官特别爱问的老朋友上。

从考点密度来看,基础数据结构和经典算法的占比最高,大概在五成左右,机器学习相关在三成,剩下的就是一些综合应用和算法分析题。这一点和很多人对算法笔试“全是 LeetCode”的想象不太一样,它更像一张复合型卷子,既要你代码能力强,也要你理论基础扎实。对准备校招的同学来说,这种结构其实是好事,因为多数考点都可以通过系统训练快速补齐。

1.2 为什么这些考点会出现在算法校招里

很多人会问,小红书这类内容平台为什么要考 KMP、堆排序和 Dijkstra?这背后的逻辑是,校招算法笔试承担的不是招“资深算法工程师”的功能,而是筛选“算法基础合格、有潜力的人”。像 KMP 这种字符串匹配算法,靠记忆也能背下来,但真正考察的是你对 next 数组递推关系的理解;堆排序和 TopK 问题对应的是海量数据处理中最基础的能力;图论最短路对应的是关系链路计算、路径推荐等真实业务场景。

再往深一层想,内容平台里有大量文本、图片、用户行为数据,算法岗入职后接触的第一件事往往是特征工程、排序模型和召回链路。这些工作不直接用到 Dijkstra,但需要你有扎实的数据结构和算法底子去处理数据清洗、索引构建、候选集合并等任务。笔试题并不追求面面俱到地模拟业务,而是通过经典算法问题看你的计算思维底子,这也是这套卷子能成为经典的原因。

2. 选择题与基础题:那些容易丢分的经典陷阱

2.1 KMP算法next数组:从手算到代码的必备技能

KMP 是算法笔试选择题里的常客,这套卷子里也出现了模式串 next 数组计算的考察。题目大意是,对于模式串 p,写出它的 next 数组。这里我们按最常见的定义来讨论:next[i] 表示 p 中从头开始长度为 i 的子串的最长相同真前后缀长度,其中 next[0] 根据教材约定可能取 -1 或 0,笔试时一定要先看清题目给出的定义再动手。

以字符串 p = "abacaba" 为例,我完整手算一遍。next[0] 按约定取 -1;i = 1 时看子串 "a",真前后缀为空,长度为 0;i = 2 时看 "ab",没有相等前后缀,长度为 0;i = 3 时看 "aba",最长相等前后缀是 "a",长度为 1;i = 4 时看 "abac",没有相等前后缀,长度为 0;i = 5 时看 "abaca",最长相等前后缀是 "a",长度为 1;i = 6 时看 "abacab",最长相等前后缀是 "ab",长度为 2;i = 7 时看 "abacaba",最长相等前后缀是 "aba",长度为 3。所以对应的 next 数组是 [-1, 0, 0, 1, 0, 1, 2, 3]。

我的经验是,遇到这类题第一件事不是急着算,而是把题目给出的 next 定义读三遍。有的题目里 next[i] 定义为“第 i 个字符匹配失败后回退的下标”,那得到的结果会和上面的数组有差异。很多丢分不是不会算,而是定义没看清。

2.2 排序算法的复杂度与稳定性速查

排序算法是选择题里的“送分题”,但也最容易因为记忆混淆丢分。我把高频排序算法整理成了一张速查表,考前值得反复默写:

算法平均时间复杂度最坏时间复杂度空间复杂度稳定性
冒泡排序O(n²)O(n²)O(1)稳定
插入排序O(n²)O(n²)O(1)稳定
选择排序O(n²)O(n²)O(1)不稳定
快速排序O(n log n)O(n²)O(log n)不稳定
堆排序O(n log n)O(n log n)O(1)不稳定
归并排序O(n log n)O(n log n)O(n)稳定
计数排序O(n + k)O(n + k)O(k)稳定

这张表要记牢,因为选择题很少只问时间复杂度,往往会把稳定性、最坏情况、空间开销混在一起设陷阱。比如快速排序在数组已经有序时会退化成 O(n²),堆排序最坏情况也是 O(n log n) 但是不稳定,归并排序稳定但需要额外 O(n) 空间。我当年就因为在“堆排序是否稳定”这个问题上栽过一次,后来每次复习排序都先背稳定性结论:稳定的有冒泡、插入、归并、计数和基数,简单选择、快排、堆排都不稳定。

2.3 数据结构细节:堆、栈、字典的实际作用

选择题里还会穿插一些数据结构细节,比如堆的插入和删除复杂度、栈的弹出顺序、哈希表的扩容机制等。常见的有:向大小为 n 的堆中插入一个元素需要 O(log n) 时间,删除堆顶也是 O(log n),但建堆有两种方式,将 n 个元素逐个插入的复杂度是 O(n log n),而用数组自底向上建堆则是 O(n)。这个问题很容易被忽略,因为大家平时直接用现成的优先队列,很少关心底层实现。

哈希表相关题目则常常考察冲突处理和负载因子。开放寻址法和链地址法各有适用场景,负载因子越大,冲突概率越高,扩容也就越频繁。很多编程语言的标准库默认负载因子在 0.75 左右,扩容时容量翻倍。这些细节虽然不起眼,但在选择题里的出现频率很高。备考时一定要把常用数据结构的底层实现过一遍,而不是只停留在 API 使用层面。

3. 核心编程题解析:从贪心到图论的实战思路

3.1 经典贪心题:区间类问题的通用解法

编程题第一道通常不会太难,常见的是区间调度或任务安排类题目。比如给一组区间,求最多能选出多少个互不重叠的区间,这类问题用贪心很好解决:先按区间右端点排序,再依次选择不冲突的区间加入结果集。排序的目的是为了让每个已选区间尽量早结束,从而为后面的区间留出更多空间。

这类题要拿满分,关键在于两点。一是能说清楚为什么“按右端点排序”比按左端点或区间长度排序更优。二是代码实现时注意边界条件,比如区间相交的判断是next_start >= current_end还是>,取决于题目定义的是开区间还是闭区间。我见过不少候选人因为边界处理差了一个等号被卡样例。实际笔试时,我会先把题目的输入输出格式看清楚,再用小数据手推一遍结果,避免代码写完后才发现理解偏差。

3.2 堆排序与TopK问题:从手写堆到快速选择

TopK 问题是算法笔试的另一个高频题,也是这套卷子里的重头戏。最直接的思路是把所有元素放进一个大小为 k 的小根堆,遍历过程中如果当前元素比堆顶大,就弹出堆顶再插入当前元素,最后堆里留下的就是最大的 k 个元素。用 Python 可以借heapq模块快速实现,但面试官很可能要求你手写堆的操作,所以向下调整和向上调整这两个过程一定要练熟。

如果数据规模特别大,内存中放不下全部数据,那就需要换思路,一种是用分治思想配合归并,另一种是使用快速选择算法。快速选择的平均复杂度是 O(n),比建堆 O(n log k) 更快,但它会改变原数组顺序,且最坏情况下也是 O(n²)。笔试时如果不要求写出最优解法,我通常建议先用最容易写对的解法拿分,有时间再优化,这样至少能保证部分评测用例通过,比直接空着强得多。

3.3 Dijkstra单源最短路:手写模板与边界

图论最短路是这套卷子里比较“硬核”的编程题。Dijkstra 算法的核心是贪心加动态规划,每次从未确定最短距离的节点中取出距离最小的节点 u,然后尝试用 u 去松弛它的所有邻居。要注意的是,Dijkstra 只适用于边权非负的图,如果题目里出现负权边,就要改用 Bellman-Ford 或 SPFA。

我用 Python 写一个堆优化的标准模板,笔试时可以参照这段结构:

import heapq def dijkstra(n, edges, start): graph = [[] for _ in range(n)] for u, v, w in edges: graph[u].append((v, w)) graph[v].append((u, w)) # 无向图加这条,有向图不加 dist = [float('inf')] * n dist[start] = 0 pq = [(0, start)] while pq: d, u = heapq.heappop(pq) if d > dist[u]: continue # 这个节点已经被更优路径更新过,跳过 for v, w in graph[u]: nd = d + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (nd, v)) return dist

这个模板里最关键的一行是if d > dist[u]: continue,也就是堆优化里的“懒删除”策略。因为堆中可能残留旧距离的节点,弹出后如果发现当前距离已经大于记录的最短距离,说明这个节点已经通过其他路径被更新过了,直接跳过即可。很多新手第一次写 Dijkstra 时忘了这个判断,导致结果错误甚至死循环。另外注意节点编号是 0 开始还是 1 开始,笔试时输入输出格式不同,数组下标很容易差一,这属于回头检查时最容易发现的低级错误。

4. 机器学习与深度学习考点:算法岗笔试的另一面

4.1 KNN与聚类:基础算法的原理与细节

这套卷子里的机器学习题,整体难度不高,但非常注重细节。比如 KNN,多数人都知道它是基于距离的惰性学习算法,但题目真正想问的往往是如何选择 K、使用什么距离度量、特征需不需要归一化。K 选太大会让决策边界过于平滑,K 选太小则容易受噪声影响,交叉验证是确定 K 的常用手段。距离度量方面,欧氏距离、曼哈顿距离、余弦相似度适用于不同场景,文本向量用余弦相似度往往比欧氏距离更合理。特征归一化这一点也容易丢分,如果各特征量纲差异大,欧氏距离会完全被量纲大的特征主导,所以要先做标准化或归一化。

聚类算法的考察重点则是 KMeans。笔试常问的两个问题是:如何确定 K 值?KMeans 一定会收敛吗?K 值可以用肘部法则结合轮廓系数判断,也可以直接用业务经验确定。KMeans 的目标函数是每个样本到其所属簇中心的距离平方和,算法通过交替更新簇分配和簇中心来最小化这个目标函数,理论上它保证收敛到局部最优,但不保证全局最优。所以面试题里如果问“不同初始中心是否影响结果”,答案是肯定的,实际工程中常通过多次随机初始化加 k-means++ 来缓解这个问题。

4.2 损失函数、梯度下降与过拟合

机器学习理论题里,损失函数和优化方法是必考项。回归任务常用均方误差,分类任务常用交叉熵。要理解为什么分类不用 MSE,是因为 softmax 加交叉熵的梯度形式更简洁,均方误差在概率输出上容易导致梯度消失的问题。梯度下降相关的考点集中在三种形态上:批量梯度下降、随机梯度下降和小批量梯度下降。三者核心区别在于每次更新使用的样本数量,批量梯度下降稳定但计算量大,随机梯度下降每步只用一个样本所以震荡大但能跳出局部最优,小批量则是两者之间的折中。

过拟合的应对方法也是高频考点,可以从数据、模型、训练策略三个层面回答。数据层面可以做数据增强、收集更多样本;模型层面可以加正则化、简化网络结构、用 dropout;训练策略层面可以早停、降低模型容量。注意答题时不能只列方法名,最好能简要说明原理,比如 L2 正则化为什么能抑制过拟合,是因为它在损失函数中加入了权重平方和,梯度下降时相当于每次都对权重做衰减,让模型倾向选择更小的参数,从而降低模型复杂度。

4.3 卡尔曼滤波与粒子群等进阶算法的考察方式

这套卷子里还出现了一些偏进阶的算法概念,比如卡尔曼滤波、模拟退火、粒子群算法。它们通常以选择题或简答题形式出现,考察的是原理理解而非手写实现。卡尔曼滤波的核心思想是“预测 + 更新”两阶段递推,它假设系统噪声和观测噪声都服从高斯分布,通过融合预测值和观测值得到最优估计。模拟退火算法则是模拟金属退火过程,用温度控制接受较差解的概率,温度越高越容易接受差解,从而跳出局部最优。

粒子群算法(PSO)更是经常被问到的智能优化算法,它模拟鸟群觅食行为。每个粒子有位置和速度两个属性,迭代时通过个体历史最优 pbest 和群体历史最优 gbest 来更新速度,再更新位置。速度更新公式里有两个关键参数 c1 和 c2,分别控制向个体最优和全局最优学习的程度,另外还有惯性权重 w,用来平衡探索和开发能力。笔试如果考到这类题目,通常不会让你写完整算法,而是考你对公式的理解,比如问“w 过大或过小时算法的搜索行为会怎样”,记住结论就能拿分:

  • w 过大,粒子飞行速度变化不明显,全局探索能力强,但收敛慢。
  • w 过小,粒子容易被群体最优吸引,收敛快,但容易陷入局部最优。
  • c1、c2 设置不当也会导致粒子震荡或提前收敛,通常取相等值即可。

5. 写代码的坑与提分技巧:复盘真实的笔试现场

5.1 输入输出处理的三个常见坑

算法笔试的编程题,代码思路对了也可能因为输入输出处理不当而大面积失分。第一个坑是不知道输入有多少组。题目常写“输入包含多组测试数据”,但在线评测系统有时一行就是一组,有时连续多行是一组,必须先按行读取再判断结束条件。第二个坑是 Python 的input()sys.stdin.readline()混用,导致读取出错,建议全程用sys.stdin配合split()解析。第三个坑是输出格式,比如“每个结果占一行”或者“结果之间用空格分隔”,多了一个空格或换行都可能导致答案错误。

我个人的习惯是写代码前先构造一个小的输入样例,手算出期望输出,再运行代码比对。这个过程只需要一分钟,但能避免大量低级错误。编程题不像 LeetCode 那样已经封装好输入输出,笔试平台的接口更原始,平时练习时不要只在 LeetCode 上刷题,建议每周至少用在线笔试系统练一次,提前适应输入输出手写的环境。

5.2 暴力解法先拿分,再逐步优化

很多同学上了笔试平台就容易陷入一种误区:只写最优解,写不出来就卡死在一道题上。我的建议恰恰相反,任何题目都先评估一下能不能用暴力解法拿到部分分。比如一张卷子有 5 道编程题,第一道可能 10 个测试点,暴力解法能过其中 6 个,优化解法能过全部 10 个,那最优策略肯定是先快速把 6 个测试点拿到手,再做后续题目,最后有时间再回头优化。

之所以强调这个策略,是因为校招笔试的判分通常是按通过的测试点数量来算的,而不是只看有没有 AC。哪怕 O(n²) 的解法在大数据量时超时,小数据量也能拿分。我见过很多候选人因为执着于写最优解,结果最简单的题都没提交成功。合理的时间分配应该是:每道题先用 10 分钟想出最直接的解法并写出来,如果剩余时间充足再考虑优化,而不是一上来就死磕最优方案。

5.3 时间复杂度的估算能力:如何快速判断代码能否跑过

在线评测平台通常会有明确的时间限制,比如 1 秒或 2 秒,C++ 在 1 秒内大概能执行 10^8 次基础运算,Python 则会慢一个数量级,大概只有 10^7 左右。这个估算能力在笔试中非常关键,因为你写代码前就应该预判自己选的算法是否能在时限内通过。比如数据规模是 10^5,O(n²) 就是 10^10 次运算,Python 绝对跑不完,必须换 O(n log n) 或 O(n) 的解法。

我看到过一个非常好的习惯:拿到每道题先看数据范围,再决定算法。n 小于等于 20 可以考虑状态压缩或暴力搜索,n 小于等于 500 可以用 O(n³) 的 Floyd 或区间 DP,n 小于等于 10^5 就需要 O(n log n) 甚至 O(n),n 到达 10^7 以上则基本只能考虑 O(n) 或更优算法。这套估算方法可以帮你快速排除掉不合适的思路,避免在错误解法的路上浪费太多时间。

6. 从笔试到面试:算法题准备的长期建议

6.1 刷题策略:按专题训练比盲目刷题更有效

关于校招算法准备,我最想分享的一点是:按专题刷题远比按题目顺序刷题更高效。这套卷子本身就体现了专题化的特点,链表、二叉树、排序、贪心、动态规划、图论各占一块。如果你今天做一道链表题,明天做一道动态规划,大脑很难形成系统的解题框架。更好的方式是用两周时间把某个专题吃透,比如这周只做二叉树的遍历、最近公共祖先、序列化反序列化,下周再做动态规划的背包类、区间类、序列类问题。

每个专题里,要把高频套路总结成模板。比如二叉树题大多基于递归,动态规划题先要定义状态再写转移方程,链表题经常会用到快慢指针和虚拟头节点。我备考时建了一个自己的算法笔记,每个专题一页,记录经典题目、模板代码、复杂度分析和易错点,考前翻一遍比临时刷几十道题都有用。校招笔试题型再变,核心套路就那些,只要专题训练足够扎实,考场上遇到新题也能快速归到已知框架里。

6.2 现场手撕代码的注意事项

进入面试环节以后,手撕代码的场景会暴露更多问题,而这些问题往往从笔试阶段就开始养成了。首先是写代码前要跟面试官确认清楚需求,比如输入是否可能为空、数组元素是否有负数、目标是最大还是最小,这些边界条件直接影响代码正确性。其次是写完代码后一定要主动跑一个简单例子,把循环和递归的过程在脑子里走一遍,这一步能发现绝大多数粗心错误。

还有一个容易被忽略的点:不要把题目解完就结束,要做复杂度分析,并思考还能不能继续优化。面试官让你做 TopK,你用堆实现了,如果补充一句“数据量特别大时可以用分布式的思路拆分到多机处理”,这就是加分项。笔试虽然看不到这种互动,但平时养成这种思考习惯,能让你在考场上写代码时更注重代码结构和边界处理,而不是只顾着套模板。

我一直觉得算法笔试的本质不是比谁见过的题多,而是比谁的基础更扎实、临场更稳定。小红书2020校招算法笔试题卷一的很多考点,放到现在依然是校招笔试的主流方向,从 KMP 到排序,从贪心到图论,从 KNN 到过拟合,每一块都值得反复练、反复梳理。备考这件事没有太多捷径,按专题一步步啃下来,多总结自己的易错点,考场上正常发挥就已经能超过绝大多数人了。最后再分享一个小技巧:做套题的时候一定要严格计时,模拟真实笔试的紧张感,因为很多人在平时刷题时能 AC,一限时就容易心态失衡,这一点越早适应越好。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询