
2023年秋招那会儿我印象最深的一场笔试就是飞猪的算法岗。说实话飞猪的笔试题量不算大但每道题都出得挺有水平既能筛掉基础不牢的人又能看出候选人有没有工程落地的感觉。当时我刷了一圈面经发现大家吐槽最多的就是“看似常规实则处处是坑”。这篇文章就把我当时参加飞猪2023届秋招算法岗笔试的全过程掰开揉碎讲一遍包括题型分布、考了哪些算法、正确的破题思路以及我踩过的坑。如果你正在准备大厂算法岗或者想了解阿里的算法岗笔试风格这篇应该能帮你少走不少弯路。飞猪算法岗笔试属于典型的“基础算法 机器学习原理 业务场景设计”组合和纯刷题型的笔试有明显区别。它不会一味考难题偏题但会在很多看似普通的考点上深挖细节比如KMP算法里next数组的手算、概率题里的公式推导、以及对模型选型的理解。这背后其实反映了一件事算法岗不只是要会写代码更要懂算法原理、能结合业务做取舍。下面我按模块拆解把值得说的细节和经验都放进去。1. 笔试整体认知与题型拆解1.1 飞猪算法岗笔试到底考什么先给没参加过的人还原一下现场。飞猪2023届秋招算法岗笔试是在统一的在线笔试平台上进行的总共约120分钟题目构成大致如下单选题/多选题覆盖机器学习基础、概率统计、数据结构与算法常识大概10到15道。编程题2到3道难度从LeetCode中等偏简单到中等偏难不等核心考点集中在字符串、动态规划、图论和排序。简答/设计题1道业务场景题通常会给一个飞猪的业务场景比如旅行推荐、酒店价格预测要求写出算法思路或技术方案。这个题型组合很典型但也有不少同学栽在时间分配上。选择题看着简单实际很容易纠结编程题如果第一道卡太久后面的题基本就没时间看。我当时就是先快速扫了一遍所有题目把选择题里拿不准的标记出来优先做编程题里最有把握的一道最后再回头啃选择题。这个策略不一定最优但至少能保证“该拿的分不丢”。1.2 考题背后的考察逻辑如果你只是把这当成一场普通的算法刷题考试那就理解偏了。飞猪算法岗笔试的每类题目都在测不同的能力数据结构与算法题考察代码基本功、复杂度的敏感度、边界条件的处理。机器学习原理题考察是否真的理解模型原理而不是只会调包。比如KL散度、ELBO、K-Means这些概念平时可能只是“听过”笔试却要你推导或计算。业务场景设计题考察工程落地思维。飞猪的业务场景和“旅行”“交易”“推荐”“定价”相关能不能把算法和具体业务结合是拉开差距的地方。我当时在准备阶段反复提醒自己刷题只是底线真正决定上限的是对算法本质的理解。就像KMP算法的next数组很多人能背代码但真让你手推一遍“abacaba”的next数组估计不少人会卡壳。笔试不考背代码考的就是你能否在纸面上把逻辑理清楚。2. 数据结构与算法高频考点逐项拆解2.1 KMP算法与next数组一道题能卡住一半人先说KMP。2023年秋招算法岗笔试里字符串匹配相关的高频考点就是KMP尤其是next数组的手算。笔试那道题我记得很清楚给出模式串 p abacaba要求写出 next 数组。这道题看起来简单但场内至少有一半人栽在了定义上。next数组的定义在不同教材里有两种约定一种表示“当前字符匹配失败后跳转的位置”另一种表示“当前字符之前的最长相同前缀后缀长度”。飞猪笔试采用的是后者next[i] 表示 p[0..i-1] 的最长相同前缀后缀长度。也就是说next[0] -1next[1] 0然后逐个递推。手推过程可以这样拆解next[0] -1约定值next[1] 0长度为1的子串没有真前后缀对于 i 2前缀 p[0..1] ab最长相等前后缀长度为0所以 next[2] 0对于 i 3前缀 p[0..2] aba最长相等前后缀是 a长度1所以 next[3] 1对于 i 4前缀 p[0..3] abac没有相等前后缀next[4] 0对于 i 5前缀 p[0..4] abaca最长相等前后缀是 a长度1next[5] 1对于 i 6前缀 p[0..5] abacab没有相等前后缀next[6] 0对于 i 7前缀 p[0..6] abacaba最长相等前后缀是 aba长度3next[7] 3所以答案就是 [-1, 0, 0, 1, 0, 1, 0, 3]。如果考场里直接给这个结果可能不到5分钟就能写完。但我当时看到很多人还在用暴力法一个个比较前后缀这就是基本功的差距。顺便放一个标准的KMP匹配代码C版本方便你对照理解#include vector #include string using namespace std; vectorint buildNext(const string p) { int m p.size(); vectorint next(m); next[0] -1; int j 0, k -1; while (j m - 1) { if (k -1 || p[j] p[k]) { j; k; next[j] k; } else { k next[k]; } } return next; } int kmpSearch(const string s, const string p) { int n s.size(), m p.size(); vectorint next buildNext(p); int i 0, j 0; while (i n j m) { if (j -1 || s[i] p[j]) { i; j; } else { j next[j]; } } if (j m) return i - j; return -1; }2.2 排序与TopK别只会调sort排序算法几乎每次笔试都会碰见但飞猪不会直接让你写一个冒泡排序完事而是会结合数据量、稳定性、内存限制来考。选择题里经常有“以下哪个排序算法是稳定的”“堆排序的时间复杂度是多少”“40亿个数找最大的100个用什么方法”这类问题。我整理了一个高频对比表笔试前可以快速过一遍排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n^2)O(n^2)O(1)稳定选择排序O(n^2)O(n^2)O(1)不稳定插入排序O(n^2)O(n^2)O(1)稳定快速排序O(n log n)O(n^2)O(log n)不稳定归并排序O(n log n)O(n log n)O(n)稳定堆排序O(n log n)O(n log n)O(1)不稳定那道“40亿个数找TopK”的题最优解不是全局排序而是维护一个大小为K的小顶堆。时间复杂度是O(n log K)空间O(K)。如果内存装不下全部数据就用外部排序堆。这个点考察的是对海量数据场景的敏感度飞猪有大量用户行为日志这类问题不是纯理论是真会遇见。2.3 贪心、动态规划与快速幂笔试实战套路动态规划和贪心在编程题里出现频率极高。飞猪笔试第二道编程题我当时遇到的是区间调度变体给定若干个带权活动每个活动有开始时间、结束时间和收益选择不冲突的活动使总收益最大。这题贪心解不适用因为带权后局部最优不等于全局最优必须用动态规划按结束时间排序设 dp[i] 表示前 i 个活动能获得的最大收益转移时用二分查找找到“最后一个结束时间小于当前活动开始时间”的活动。这类题的通用破题套路是先判断是贪心还是DP——如果局部最优能达到全局最优就选贪心否则考虑DP。判断完之后写状态转移时重点关注“不选当前元素的情况”很多人丢分都丢在遗漏 dp[i-1] 这个不选分支。快速幂也是笔试的常客。比如计算 a^b mod mb 可以达到 10^18 级别。Python 里可以直接 pow(a, b, m)但笔试选择题会要求你判断时间复杂度或者让你填充代码。模板如下def fast_pow(a, b, m): res 1 a % m while b 0: if b 1: res (res * a) % m a (a * a) % m b 1 return res快速幂的核心思想是把指数折半把幂运算从 O(b) 降到 O(log b)。同样的思路还可以用在矩阵快速幂、斐波那契数列求第 n 项等场景。2.4 图论与搜索Dijkstra、二分图HK算法图论基础算法每年都有。飞猪业务里涉及大量路径规划、运筹优化所以 Dijkstra 这类最短路算法属于必须掌握的。笔试真题里有一道题需要求一个无向加权图中从起点到终点的最短路径数据范围不算大用优先队列优化后的 Dijkstra 可以轻松通过。Dijkstra 的实现其实有固定的套路import heapq def dijkstra(graph, start): n len(graph) 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]: if dist[u] w dist[v]: dist[v] dist[u] w heapq.heappush(pq, (dist[v], v)) return dist另外一个比较容易被忽视的是二分图相关的算法。热搜词里的HK算法Hopcroft-Karp算法就是二分图最大匹配的优化版本核心思想是用BFS构建增广路径层级图再用DFS寻找增广路时间复杂度比匈牙利算法的O(VE)优化到O(E√V)。笔试一般不要求你手写HK但选择题可能会问匈牙利算法和HK的区别或者给一个二分图问最大匹配数。知道“为什么用HK——减少增广路的搜索次数”就够了。3. 机器学习与智能算法笔试中的“算法”不只是数据结构3.1 经典ML原理KNN、聚类、逻辑回归、集成学习飞猪算法岗笔试很大一部分选择题和简答题会落在机器学习基础。这些题不像编程题那么费脑但特别考验概念是否扎实。我当时遇到的几个高频考点KNN的K值影响、K-Means的收敛条件、逻辑回归的损失函数推导、XGBoost和GBDT的区别。KNN这题问的是“K值增大时模型的偏差和方差如何变化”。答案是K值增大模型变得越简单偏差增大、方差减小。这个反直觉的点很常考因为很多人直觉认为“样本用得越多越准”但实际上K太大时会把距离很远的样本也纳入投票导致边界过于平滑。K-Means的题则经常问“初始k个中心点如何选择”。标准答案是随机选择但随机选择容易陷入局部最优所以实际工程会用K-Means先随机选第一个中心点再按距离平方加权概率选择后续中心点让初始中心点尽可能分散。这个细节在面试里也经常被追问。逻辑回归那边手写梯度下降更新公式是常规操作。逻辑回归的损失函数是交叉熵L -(1/N) * Σ [y_i * log(p_i) (1 - y_i) * log(1 - p_i)]其中 p_i 1 / (1 exp(-w·x_i))。对 w 求梯度后得到∂L/∂w (1/N) * Σ (p_i - y_i) * x_i所以梯度下降更新就是 w : w - lr * (1/N) * Σ (p_i - y_i) * x_i。考场里能写出这一步基本就能说明你是理解逻辑回归而不是只会 import。集成学习这块飞猪笔试比较偏爱“随机森林和GBDT的区别”。一个核心区别是随机森林是Bagging并行训练多棵树然后投票或平均GBDT是Boosting串行训练每棵树拟合前面的残差。XGBoost在GBDT基础上加了二阶泰勒展开、正则项和列采样训练速度和精度都更好。这类题不需要你写公式推导但需要能说清楚“为什么”。3.2 概率与信息论KL散度与ELBO的推导套路KL散度和ELBO是算法岗笔试中偏难的一类题。很多人看到这两名词就头大但飞猪2023年笔试确实考了而且不是简单的概念判断题而是让你写出KL散度的定义式并解释它在变分推断里的作用。KL散度的定义式是KL(P || Q) Σ P(x) * log(P(x) / Q(x))注意KL散度不对称KL(P||Q) ≠ KL(Q||P)所以它不是一个真正的距离度量。笔试选择题经常挖这个坑。ELBO的推导其实是变分推断的核心。我们想最大化证据 log P(X)但直接算很难因为要积分掉隐变量 Z。于是引入一个变分分布 q(Z)利用Jensen不等式得到log P(X) ≥ E_{q(Z)}[log P(X, Z) - log q(Z)]右边的期望就是ELBO。它等于ELBO E_q[log P(X|Z)] - KL(q(Z) || P(Z))这个式子很有用最大化ELBO等价于在“拟合数据”和“逼近先验”之间做权衡。笔试如果只考到一个层面你写出ELBO的分解式并解释“第一项是重建似然第二项是正则项”就足够了。如果面试追问再往VAE上引。我当时复习的时候特意把KL散度、ELBO、EM算法串起来理解EM算法里E步就是在固定参数时计算隐变量的后验分布本质上也和KL散度有关。把这些点串成一条线比零散背诵效率高很多。3.3 智能优化算法粒子群、模拟退火、遗传算法热搜词里频繁出现“粒子群算法原理”“模拟退火算法”这说明这类智能优化算法在算法岗笔试中的出镜率不低。它们的定位是当问题规模大、或者目标函数不可导时传统梯度方法失效需要借助启发式搜索。粒子群算法PSO的核心是模拟鸟群觅食每个粒子有位置和速度更新时受两个因素影响——个体历史最优 pbest 和全局历史最优 gbest。速度更新公式v_i w * v_i c1 * r1 * (pbest_i - x_i) c2 * r2 * (gbest - x_i) x_i x_i v_i这里 w 是惯性权重c1 是自我认知系数c2 是社会认知系数。笔试选择题常考“w过大会怎样”——答案是全局搜索能力强但收敛慢w过小则容易陷入局部最优。模拟退火算法的核心是Metropolis准则在退火过程中当新解更优时一定接受更差时以一定概率接受概率随温度降低而减小。概率公式是 exp(-ΔE / T)。这个“以一定概率接受差解”的设计目的是跳出局部最优和贪心算法“只接受更优解”的机制完全不同。这类题飞猪笔试一般不会让你写完整代码而是放在选择题中让你判断“这种做法体现了什么思想”。答这类题的关键不是死记步骤而是理解每种优化算法解决的核心问题。3.4 控制与信号类算法PID、卡尔曼滤波、FOC你可能会觉得奇怪算法岗笔试为什么会出现PID、卡尔曼滤波、FOC这些偏控制领域的算法。其实不少大厂算法岗也会涉及IoT设备、硬件数据、时序信号处理所以这些词出现在热搜里不是偶然。PID算法笔试考得最多的是增量式PID公式Δu(k) Kp * (e(k) - e(k-1)) Ki * e(k) Kd * (e(k) - 2e(k-1) e(k-2))笔试选择题常问如果系统响应太慢应该增大哪个参数答案是增大Kp或适当增大Ki。如果系统超调严重、震荡频繁应该增大Kd来抑制变化。卡尔曼滤波则是状态估计算法笔试高频考点是“预测—更新”两步套路预测x_pred F * x_prevP_pred F * P_prev * F^T Q更新K P_pred * H^T * (H * P_pred * H^T R)^(-1)x_new x_pred K * (z - H * x_pred)P_new (I - K * H) * P_pred不需要你把矩阵写完但要能说清楚Q和R分别代表过程噪声和观测噪声的不确定性K卡尔曼增益决定了更相信模型预测还是更相信传感器观测。如果R很小说明观测噪声小卡尔曼增益会偏大滤波器更信任观测值。4. 实操过程从读题到AC的完整复盘4.1 笔试环境与流程提前踩点更安心飞猪当时用的是常见的在线笔试平台支持C、Java、Python等主流语言但要注意平台不会帮你自动补全代码也没有本地IDE那么智能。很多人平时习惯用PyCharm或VSCode到了笔试平台连个括号匹配提示都没有写起来特别别扭。我建议在正式笔试前至少花半天时间去熟悉平台的代码编辑器。具体来说试一下缩进是空格还是Tab、错误提示怎么看、有没有“测试用例”按钮、代码是单文件提交还是多文件。我当时就吃过亏有一道题里要读一行包含空格的字符串平台自带的输入示例是用“\n”分隔的我一开始没看清导致解析错误。另外要注意语言选择。Python写起来快但某些平台对Python的支持比较“原始”递归层数会被限制如果赶上DFS或者递归DP的题很可能会爆栈。我当时遇到一道题第一反应是用递归做动态规划写完后本地通过平台却报“栈溢出”改成自底向上的递推才过。这个细节值得提前知道。4.2 真题一手推KMP的next数组题目还原给定模式串 p abacaba写出它的next数组并说明其中next[6]的含义。解题过程明确next数组定义next[i]表示p[0..i-1]的最长相等前后缀长度。逐项计算next[0]-1, next[1]0, next[2]0, next[3]1, next[4]0, next[5]1, next[6]0, next[7]3。next[6]的含义是当匹配到p[6]失败时模式串应该回退到位置0因为next[6]0即从头开始重新匹配。如果把 next[6]3则说明p[0..2]p[4..6]aba匹配失败时可以跳到位置3保留已经匹配的“aba”前缀。这个答题思路在笔试里很占便宜不是只写答案还写清楚推导过程和含义面试官能一眼看出你是真懂还是背题。4.3 真题二带权活动选择动态规划题目还原有 n 个活动每个活动有开始时间 s_i、结束时间 e_i 和收益 v_i选择不冲突的活动求最大总收益。n ≤ 10^5时间范围 0 ~ 10^9。这是一道典型的“加权区间调度”问题。贪心选结束时间最早只能保证数量最多不能保证收益最大。正确做法是按结束时间 e_i 升序排序。定义 dp[i] 为前 i 个活动能获得的最大收益。转移方程dp[i] max(dp[i-1], dp[p[i]] v_i)其中 p[i] 表示“最后一个结束时间 s_i”的活动编号。因为数组已按结束时间排序所以 p[i] 可以用二分查找在 O(log n) 时间内找到。核心代码如下Pythonimport bisect # activities [(start, end, value)] activities.sort(keylambda x: x[1]) n len(activities) starts [a[0] for a in activities] ends [a[1] for a in activities] dp [0] * (n 1) for i in range(1, n 1): s, e, v activities[i-1] # 找到最后一个结束时间 s 的活动 j bisect.bisect_right(ends, s, 0, i - 1) dp[i] max(dp[i-1], dp[j] v) print(dp[n])这道题我当时的失误是忘记给活动按结束时间排序就开始写转移方程写到一半发现不对又回来改。所以强烈建议看到区间类DP第一步永远是排序不是急着设状态。4.4 真题三酒店价格预测的业务设计题题目还原如果你要为飞猪上一个“酒店未来30天价格预测”的功能你会怎么做要求给出技术方案、特征设计和评估指标。这类开放题没有标准答案但答题框架很重要。我当时的回答思路是三层第一层问题拆解。酒店价格预测本质上是一个时间序列预测问题但又有特殊性——价格受节假日、供需关系、竞对价格、用户预订行为影响不是简单ARIMA能解决的。第二层方案选型。短中期预测可以用LightGBM/XGBoost把时间特征星期几、是否节假日、距出行日天数、酒店特征星级、评分、历史价格、市场特征周边同等级酒店平均价格、搜索热度作为特征输入。如果需要捕捉长期依赖再上LSTM或Transformer但实际业务里树模型往往性价比更高、更容易解释。第三层评估指标。价格预测的误差评估不能用单一指标。我当时写的是整体用MAPE平均绝对百分比误差但它对低价格酒店很不友好价格100元的酒店差50元和价格1000元的酒店差50元MAPE差异巨大。所以可以补充WAPE加权绝对百分比误差或者分价格段评估。这道题其实考察的是“能不能把一个宽泛的问题转化为可执行的算法方案”。平时如果只刷LeetCode遇到这种题容易手足无措。建议多积累几个常用业务场景的算法方案比如推荐排序、价格预测、销量预估、异常检测。5. 常见问题与避坑技巧实录5.1 时间不够怎么办优先级与取舍飞猪笔试的总时间是固定的但很多人在选择题上花费过多时间导致编程题仓促收尾。我看到的普遍情况是选择题里有几道机器学习推导题比如KL散度的变形、梯度公式的推导容易让人纠结。我的建议是把这类题控制在每题2分钟内如果超过2分钟还没有思路先标记跳过去。合理的优先级是会做的编程题 会做的选择题 会做一半的编程题 纠结的选择题。编程题一道完整AC的分值往往顶好几道选择题所以哪怕放弃一两道选择题也要保证编程题有充分的调试时间。5.2 边界条件与数据范围最容易翻车的地方笔试翻车最常见的不是思路不会而是边界条件没处理。比如KMP的 next 数组很多人计算到中间就忘了 next[0] 的约定动态规划的状态数组往往会多开一位结果初始化写错二分查找的边界条件更是重灾区。我的经验是写完代码后不要急着提交先用三组数据进行自测——最小输入比如n1、极端输入比如所有区间都重叠、随机输入。这大概多花3分钟但能避免大量无谓的罚时。飞猪笔试平台支持自测用例一定要用起来。还有一个容易被忽略的点数据范围决定算法选型。如果 n ≤ 10^5O(n^2) 的算法基本会超时如果 n ≤ 20可以考虑状态压缩DP和搜索。做题前先看一眼数据范围再决定写哪种复杂度的算法这是基本功。5.3 “算法岗笔试是不是只看编程”的常见误区不少准备秋招的人以为算法岗笔试就是刷题把精力全投在LeetCode上结果到了考场发现还有大量机器学习选择题和场景设计题一下子就懵了。飞猪这场笔试就很典型编程题只占一部分还有不少选择题在考概率统计、模型原理。我的建议是准备算法岗笔试要双线并行一条线是数据结构与算法刷题重点突击字符串、DP、图论、贪心另一条线是机器学习基础复盘手推逻辑回归、K-Means、KNN、KL散度、集成学习这些高频考点。尤其到了秋招后期大厂笔试越来越重视对算法原理的理解这是趋势。5.4 刷题准备岗位匹配的复习路线结合飞猪的业务场景旅行推荐、价格预测、搜索排序、供需预测我给准备投飞猪算法岗的同学划个复习重点必刷算法题字符串匹配KMP、区间DP、背包DP、最长上升子序列、TopK、Dijkstra、并查集。必会ML模型逻辑回归、决策树/随机森林/GBDT/XGBoost、K-Means、KNN、朴素贝叶斯。必懂数学概念KL散度、极大似然估计、贝叶斯公式、期望/方差、正态分布。可以了解但不用死磕粒子群、模拟退火、遗传算法等智能优化算法知道核心思想即可。有时间再扩展PID、卡尔曼滤波、FOC这些偏控制/信号的算法出现概率较低但一旦出现就是区分度很高的题。按这个路线准备既能覆盖大部分考点又不至于陷入无意义的题海。飞猪笔试里那几道让我印象深刻的题事后复盘其实都在这条路线的覆盖范围内。如果你能把上述内容真正吃透就算题型换一换也基本能稳住。