ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

途虎养车秋招算法笔试B卷解析:KMP、Dijkstra与业务场景考点全梳理

2026/8/29 11:47:23 拓冰建站 浏览量
途虎养车秋招算法笔试B卷解析:KMP、Dijkstra与业务场景考点全梳理 收到这份“途虎养车2023秋招算法笔试试卷B”的题目时我第一反应是标题很直白但真正的信息量在“B卷”这两个字上。经历过秋招的人都懂同一家公司的A卷和B卷表面上是平行换题背地里往往代表着完全不同的岗位偏好和能力考察侧重点。尤其是途虎养车这种自带汽车后市场业务场景的公司它的算法笔试不会只考纯LeetCode题一定会往实际业务上靠。这篇文章不打算做“灵光一现”式的考点罗列而是以B卷为切入点把算法岗笔试里那些真正决定你能不能进面试的东西掰开揉碎讲清楚考了什么、为什么考、怎么答才能踩在出题人的得分点上。1. 从试卷看算法岗的真实考点题型结构与业务底色1.1 试卷B的整体形态与题型分布途虎这套B卷整体给我最深的印象是它不追求题目“难到你写不出来”而是追求“你以为你会写但一写就漏”。整张卷子如果按常见秋招算法笔试的节奏来换算大概可以分成四块明显区隔的内容。选择题和填空题大约占了20%到30%的分值覆盖的范围包括数据结构基础、排序算法复杂度、字符串匹配、图论概念、机器学习基本概念。这一部分没有太多弯弯绕绕但有个非常明显的倾向——它喜欢考“手算能力”。比如KMP的next数组、给定一组数让你写堆排序每一轮的结果、或者给定事务让你判断满足第几范式都属于那种你平时用IDE写代码时从不经大脑但手算起来却容易翻车的类型。然后是代码题一般两道左右排在试卷中段。途虎的代码题很少出那种“背诵型”的原题更多是在经典模型外面套一层业务壳。比如给一批门店坐标、库房坐标和订单时效让你求最短配送路径或者给一个维修工单的依赖关系让你求合理的调度顺序。这类题目剥掉壳之后核心还是图论、贪心、动态规划这些老伙计但如果你被业务描述带偏了没有迅速抽象出裸模型就会浪费大量时间。最后是场景设计题和简答题。这一块是B卷区别于A卷的关键也是很多刷题型选手最容易懵的部分。它不会要求你写完整代码但会给你一个现实问题比如“如何预测门店未来一周的保养订单量”“如何给不同用户推荐合适的轮胎套餐”然后让你描述思路、指标、特征、模型选型和评估方式。说白了考察的是你有没有用算法解决真实业务问题的习惯而不只是刷题的手感。1.2 途虎的业务场景决定了考题的“味道”为什么途虎的卷子会带着明显的业务味道因为它不是一家纯互联网公司而是汽车后市场的产业互联网公司。汽车后市场有个特点链条长、参与角色多、决策重、低频但客单价高。用户在途虎上买轮胎、做保养、选门店、约技师背后涉及供应链库存、门店承接能力、技师排班、价格策略、物流配送、售后风控一整条链路。所以途虎算法团队的方向基本覆盖这几个领域搜索推荐、销量预测、动态定价、供应链优化、路线调度、风控反作弊、智能客服、故障诊断。这些方向在笔试里会以各种变体出现。比如搜索推荐方向的候选人是KNN、聚类、排序模型供应链方向的人会看到库存预测和路径规划风控方向则可能出现异常检测、规则引擎甚至图算法。我在做这套卷子的时候最大的感受是它不要求你每个方向都精通但要求你对常见算法的基础原理和适用场景门儿清。这不是那种“一招鲜吃遍天”的纯竞赛卷更像是一张在试探你“能不能把算法用在正经业务上”的筛网。1.3 A卷与B卷的分工逻辑A卷和B卷是秋招常见的平行试卷出题逻辑往往是岗位方向区分。A卷可能更偏向推荐、搜索算法方向B卷则更偏向供应链、定价、调度这类偏工程落地的方向。当然具体到不同年份可能有变化但从B卷的题目构成来看它对工程能力的考察明显重于对模型深度的考察。这意味着什么意味着如果你只是把注意力放在“我这个模型能不能再提点精度”上反而容易忽略工程实现和业务指标。但途虎这类产业互联网公司恰恰非常看重算法能不能真正上线、能不能扛住真实数据量、能不能跟现有系统集成。所以B卷里出现Dijkstra、KMP、Kahn排序这类经典工程算法的频率可能会比Transformer的变体还要高。备考时一定不能只盯前沿模型经典算法的基础反而更值钱。2. 数据结构与基础算法看着简单却最拉分的题目2.1 字符串问题KMP与next数组的手算能力KMP算法在B卷里的出镜率相当高而且出题方式往往是填空题形式。最经典的考法就是模式串pabacaba让你求next数组。这里有个很坑的地方——不同教材对next数组的定义并不完全统一考题里如果括号里明确写了next[i]的定义你必须严格按它的定义来算如果没写默认按“最长相等前后缀长度”处理。以模式串pabacaba为例我用最常用的定义来手算一遍。next[i]表示p的前缀子串p[0..i]的最长相等前后缀长度且前缀不能取整个子串本身。i0子串a最长相等前后缀长度为0next[0]0。i1子串ab前缀a后缀b不相等next[1]0。i2子串aba前缀a和后缀a相等前缀ab和后缀ba不相等最长长度为1next[2]1。i3子串abac前缀a和后缀c不相等前缀ab和后缀ac不相等前缀aba和后缀bac不相等next[3]0。i4子串abaca前缀a和后缀a相等前缀ab和后缀ca不相等最长长度为1next[4]1。i5子串abacab前缀ab和后缀ab相等长度为2再往前aba和后缀cab不相等next[5]2。i6子串abacaba前缀aba和后缀aba相等长度为3next[6]3。所以这个模式下按最长相等前后缀定义的next数组就是[0, 0, 1, 0, 1, 2, 3]。但如果你用的是“失配时跳转位置”的版本结果会整体右移并补一个-1得到[-1, 0, 0, 1, 0, 1, 2]。很多人在这一步丢分不是因为不会KMP而是因为没看清题目给的定义。我的建议是考前把这两种定义都亲手算三遍尤其是“abacaba”这种带重复前缀的经典串闭着眼都要能写出来。2.2 排序与TopK复杂度权衡比“能跑通”更重要排序是B卷的常驻考点但它考的方式会比较刁钻。它不会直接问你“快排的时间复杂度是多少”而是给你一段具体场景让你选最合适的排序算法。比如“10亿条日志记录按时间排序内存只有1GB应该用什么方法”“数据基本有序但偶尔有逆序对用什么排序最快”。这类问题背后考的是时间复杂度和空间复杂度的权衡。经典的排序算法对比我直接给出一张表这张表笔试前一晚值得过一遍排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序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(1)不稳定归并排序O(n log n)O(n log n)O(n)稳定计数/基数排序O(nk)O(nk)O(k)稳定TopK问题在这类试卷里几乎是必考。最容易被忽略的是“海量数据TopK不能用快排全排序”正确的切入点是堆。维护一个大小为K的最小堆遍历数据时如果当前元素比堆顶大就替换堆顶并调整堆最终堆里就是最大的K个元素。这个过程时间复杂度是O(n log K)空间O(K)适合单机海量数据场景。但如果你面对的是分布式海量数据还得加一层分治每台机器算局部TopK然后汇总再算全局TopK。笔试题里如果出现“分布式”“数据量级PB”这些字眼答案就往分治堆方向靠。2.3 图论三兄弟Dijkstra、Kahn与二分图匹配图论部分的考察在B卷里相当务实。Dijkstra处理最短路径Kahn处理拓扑排序HK算法处理二分图最大匹配这三者几乎覆盖了途虎业务里最常见的三类调度问题。Dijkstra的代码题考察点不难但有个容易踩的细节图是稠密图还是稀疏图决定了你该用朴素版本还是堆优化版本。朴素Dijkstra的时间复杂度O(V^2)堆优化版本O(E log V)。如果题目里节点数上万、边数十几万你用朴素版本大概率超时。堆优化的核心是松弛操作时用优先队列弹出当前距离最小的未访问节点我习惯的实现是这样import heapq def dijkstra(graph, n, start): 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。不加这一行优先队列里那些已经过期的节点还会继续松弛复杂度会退化很容易被笔试里的极限数据卡超时。Kahn算法解决的是有向无环图的拓扑排序它跟途虎的维修工单调度、供应链任务依赖关系很契合。维护一个入度为0的节点队列逐个弹出并减少邻居的入度新的入度为0的节点再入队。如果最终弹出的节点数小于总节点数说明图里有环。这个算法我在笔试里最喜欢用因为它代码量小、思路清晰而且能顺便证明环的存在。二分图匹配的HK算法在途虎场景里可以对应“订单与技师匹配”“用户与优惠券匹配”这类问题。如果笔试里出现“最多能同时完成多少对匹配”这类字眼基本就是二分图最大匹配。考题如果节点规模小直接用匈牙利算法就能过节点规模大才需要HK算法把复杂度从O(VE)优化到O(E sqrt(V))。笔试建议第一版先写匈牙利算法拿到基础分再考虑优化。3. 搜索、优化与机器学习试卷里暗藏的“业务型算法”3.1 启发式搜索粒子群与模拟退火考察的是建模能力说实话第一次看到卷子里出现粒子群算法和模拟退火的时候我愣了一下因为这不是传统算法笔试的常规菜品。后来想明白了途虎的业务里有很多非线性组合优化问题比如仓库选址、技师排班、优惠券发放策略这些问题的解空间大、约束多、目标函数复杂不适合用精确算法求解启发式搜索反而才是工程上的常规解法。粒子群算法的核心逻辑并不复杂把每个候选解看成一只有位置和速度的粒子每轮迭代中粒子向自己的历史最优位置和群体的全局最优位置方向飞行速度更新公式里包含惯性权重、自我认知项和社会认知项三个部分。笔试如果考到这个往往不是让你默写公式而是给你一个优化目标让你解释为什么粒子群适合解决这类问题以及相比网格搜索有什么优势。回答的关键点落在粒子群不需要目标函数可导不依赖梯度信息适合解空间大且非凸的问题。模拟退火则更常用于离散组合优化。我记得有一道类似“门店选址要覆盖最大化同时成本最小化”的场景题最优做法就是用模拟退火做随机搜索配合Metropolis准则按概率接受更差的解从而跳出局部最优。这部分值得注意的是温度下降的速度直接影响结果质量降温太快容易陷入局部最优降温太慢则耗时无法接受笔试里如果让你写伪代码记得把“初始温度、降温系数、终止温度”三个参数交代清楚这本身就是得分点。3.2 控制类算法的跨界从PID到卡尔曼滤波热词里出现了PID和卡尔曼滤波这在做纯互联网业务的人看来可能有点突兀但在途虎这种有线下实体、有硬件设备、有车辆数据的公司这类控制类算法其实是有应用场景的。比如门店环境监控、智能洗车设备参数调节、车辆传感器数据的平滑去噪。PID算法的原理是比例、积分、微分三个环节的加权控制。比例项响应当前误差积分项消除稳态误差微分项抑制超调。笔试如果考到更大的可能是一道概念题给定一个控制场景让你分析三个参数分别调大调小会有什么影响。回答时抓住几个关键结论Kp太大会震荡Ki太大会超调Kd对噪声敏感。卡尔曼滤波则是更进阶的考点。它的核心思想是在存在测量噪声的情况下用系统状态方程做预测再用实际测量值做校正最终得到最优状态估计。笔试不要求你推导完整个公式但你至少要能说出它的两个阶段——预测和更新以及协方差矩阵在里面的作用。如果面试官再追问“卡尔曼滤波为什么比单纯均值滤波好”答案是它利用了系统的动态模型能根据上一时刻状态预测当前时刻状态再用观测值做修正在噪声非高斯或系统非线性的场景下它比简单滤波有更强的适应能力。3.3 机器学习基础题从KNN、聚类到BM25B卷的机器学习部分考得很基础但覆盖面广。KNN是高频出现的一个模型考察点集中在三个方面k值选择、距离度量、决策规则。k值太大会让模型过于平滑太小则容易过拟合通常用交叉验证来选择。距离度量在数值特征上用欧氏距离文本场景下可能用余弦相似度。笔试里如果给你一个具体例子让你判断样本类别别上来就算欧氏距离先看看特征类型和量纲是否一致如果量纲差异巨大答案大概率是“先做标准化再算距离”。聚类算法里最常考的是K-Means。它有两个坑一是初始中心点选择对结果影响很大所以工程上会用K-Means或多次随机初始化二是K值怎么定常用肘部法则和轮廓系数。如果笔试给出一个“从用户行为中划分人群”的场景题答题框架一般是先特征工程再用K-Means聚类然后用轮廓系数评估最后给每个簇打业务标签。BM25在途虎这类有搜索业务的场景里很重要它本质上是给文档和查询之间的相关性打分。BM25的核心思想是词频不能是线性的文档长度要归一化还要考虑逆文档频率。笔试如果考到最常见的题型是给你一个查询词和两篇文档手工算BM25得分或者解释为什么比TF-IDF效果好。准备这部分时建议把BM25公式里的k1和b参数的含义背清楚k1控制词频饱和度b控制文档长度归一化力度。这两个参数不是拍脑袋定的通常通过网格搜索调优。3.4 规则引擎的RETE算法工程场景下的匹配效率问题热词里出现了Drools的RETE算法这让我有点意外但也说明试卷覆盖了工程类算法。途虎的业务里有大量风控规则、优惠规则、审核规则规则一多逐条匹配的效率就会成为瓶颈RETE算法就是专门解决规则匹配效率的。RETE算法的核心是把规则拆成条件网络构建Alpha网络和Beta网络。Alpha网络负责事实的原子条件匹配Beta网络负责跨条件的连接匹配。当一个事实对象进入系统它会在Alpha网络中逐层过滤只有通过全部条件的事实才会进入Beta网络参与连接。对于多个事实的共同匹配RETE通过记录中间匹配结果来避免重复计算。笔试如果考RETE更多是概念层面的理解比如“为什么RETE算法能提高规则匹配速度”回答要点就是模式共享、中间结果缓存、避免全量规则重复扫描。4. 代码题的完整解题链路从读题到边界条件的复盘4.1 如何在一分钟内拆解一道题目很多人在笔试里最浪费时间的地方不是写代码而是读题。尤其是业务壳很厚的题比如“仓库里有若干种维修配件每种配件有数量、体积、有效期现在要给若干门店配货每辆运输车有容量限制求能配送的最大门店数量”这道题如果按字面意思去模拟复杂度会非常难看。但如果把“配件”抽象成“物品”“门店需求”抽象成“背包容量”“能配送的最大数量”抽象成“最多能装满多少个背包”问题就变成了多重背包变形问题。我的经验是看到题目先不急着想解法先用一分钟做三件事。第一件事划掉所有业务名词把题目里的“门店”“仓库”“技师”“订单”全部替换成图、节点、边、权重、集合这一层抽象。第二件事看数据范围。n小于20大概率是状态压缩DP或搜索n小于1000是O(n^2)动态规划n小于1e5就要想O(n log n)甚至O(n)出现1e9这种数字基本说明要用数学方法或二分答案。第三件事立刻想这个题属于哪类经典问题最短路径、拓扑排序、区间DP、背包、二分图匹配还是贪心。抽象对了解法就自然浮现了。4.2 KMP编程实现的三个易错点KMP的手算next数组只是第一步真正写代码时还有三个很隐蔽的坑。第一个坑是求next数组时循环下标的边界。很多写法是把模式串和自己做匹配用i遍历主串位置用j记录当前已匹配的前缀长度核心逻辑是当p[i]不等于p[j]时j需要回退到next[j-1]而不是next[j]。这里很容易因为数组越界写错。第二个坑是在主串中搜索时模式串匹配完成后j要回到next[j-1]让已经匹配的前缀部分继续参与下一轮匹配否则会漏掉重叠匹配的情况。第三个坑是next数组的定义不同代码逻辑会跟着变用最常用的“最长相等前后缀长度”版本写出的代码和用“失配跳转位置”版本写出的代码在回退逻辑上并不完全一致考试时一定要先确认定义再动手。我建议笔试时用一个自己最熟悉的固定模板不要临场改。下面这个模板是我个人常用的“next数组 最长相等前后缀长度”版本def build_next(p): n len(p) next_arr [0] * n j 0 for i in range(1, n): while j 0 and p[i] ! p[j]: j next_arr[j - 1] if p[i] p[j]: j 1 next_arr[i] j return next_arr def kmp_search(text, pattern): n, m len(text), len(pattern) if m 0: return 0 next_arr build_next(pattern) j 0 for i in range(n): while j 0 and text[i] ! pattern[j]: j next_arr[j - 1] if text[i] pattern[j]: j 1 if j m: return i - m 1 return -14.3 Dijkstra的堆优化复杂度如何从O(V^2)到O(E log V)朴素Dijkstra每轮都要从所有未访问节点中挑出距离最小的节点这需要O(V)的扫描整体O(V^2)。堆优化的思路是用优先队列维护候选节点堆顶就是当前距离最小的节点弹出、松弛、再入堆看起来简单但复杂度分析要特别注意每个节点可能被多次入堆所以堆操作次数上限是O(E)总复杂度O(E log V)。在稀疏图里这个优化效果非常明显在稠密图里反而可能因为堆操作常数而变慢。笔试中如果遇到多源最短路径问题比如有多个仓库要算每个门店到最近仓库的距离可以建一个虚拟源点把它到所有仓库的距离设为0然后在这个虚拟图上跑一次Dijkstra。这个小技巧很好用而且很多改卷老师会认可这个做法。4.4 边界条件与测试用例设计50%的通过率和100%的区别代码题不是跑通样例就完事了笔试系统往往有一堆隐藏用例等着卡你。我见过太多人样例一把过提交却只有50%甚至更低问题基本都出在边界。常见的边界条件有这么几类输入为空或只有一个元素图为空或单节点图包含负权边注意Dijkstra此时不适用字符串全空或模式串比主串长节点编号从0开始还是从1开始数组里全是负数目标值等于某个现有元素数据量达到n的极限导致O(n^2)超时。这些边界条件在平时刷题时就要形成条件反射每写完一个函数先别急着提交自己设计五组用例空、最小规模、最大规模、极端值、重复值全部跑过再交。还有一个经验是笔试里多花一分钟设计测试用例能省掉一遍提交的等待时间。很多平台提交后要等队列反复提交会耗尽宝贵的时间。5. 笔试时间分配与常见失分点过来人的应试策略5.1 90分钟的答题节奏选择题不要恋战如果按常见的90分钟作答时间来算我推荐的节奏是选择题和填空题25到30分钟一题最多2到3分钟卡壳超过这个时间立刻跳过先标记回头再看。代码题两道一共40到50分钟第一道简单题控制在15分钟内跑通第二道难题留足30分钟。最后留10到15分钟给场景设计题。选择题最忌讳的是恋战。一道KMP手算题如果算了4分钟还没算出来说明你的计算过程有问题先跳过去做后面的代码题代码题分值更高性价比明显更划算。如果时间富余再回头算。5.2 这些“送分题”其实最容易丢分很多丢分点并不是难题而是简单题里的“想当然”。排序算法稳定性是一个重灾区。很多人只记得快排不稳定、归并稳定但被问到“选择排序为什么不稳定”的时候就含糊了——因为选择排序在交换时可能把相同元素的相对位置打乱。时间复杂度分析里容易被忽略的是递归算法的空间复杂度归并排序的空间复杂度是O(n)快排是O(log n)很多人只背时间不背空间结果场景题里被“内存受限”绊倒。还有一个隐藏失分点是“回答不完整”。比如问“Dijkstra算法的前提条件是什么”只回答“图中不能有负权边”是拿不到满分的还要回答“如果是负权边应该用什么算法”哪怕只是简单补充一句“负权边要改用Bellman-Ford”也能体现知识体系的完整度。答题时把相关的正向条件和边界条件一起说分数会明显更好看。5.3 场景设计题的答题套路需求分析、指标定义、方案选择、落地风险场景设计题是最能拉开差距的部分它没有标准答案但有一套标准的答题骨架。我自己在笔试中摸索出的框架是四步走。第一步是需求分析。把业务问题翻译成算法问题明确输入是什么、输出是什么、约束有哪些。比如“预测未来一周保养订单量”输入是历史订单数据、车型数据、季节因素、营销活动日历输出是每天的订单量区间约束是数据量的粒度是门店级还是城市级。这一步的目的是让阅卷人知道你是真的理解了业务而不是套了一个黑盒模型。第二步是指标定义。预测问题用什么指标衡量回归用MAE还是RMSE分类用精确率还是召回率、F1排序问题用NDCG还是AUC。指标定义能反映你对业务目标的理解深不深。预测订单量场景如果只给RMSE一个指标会被认为考虑不周应该补充“低库存预警的召回率”这类业务指标。第三步是方案选择。给出基础方案和进阶方案。基础方案可以用线性回归、XGBoost这类成熟模型进阶方案可以提到时序模型、图神经网络或者多任务学习框架。这里不需要写完整数学推导但要点出每个方案的适用条件和计算复杂度。第四步是落地风险。这一点最容易被忽略但也是最容易加分的。比如预测模型上线后面临数据漂移怎么处理、冷启动阶段数据量不足怎么办、离线指标和线上指标不一致怎么排查。把这些风险点说出来阅卷人会觉得你是真的想过“把算法做成产品”而不是只做实验。5.4 秋招笔试的备考工具箱刷题之外的准备笔试前一周除了刷题我强烈建议做几件“软性准备”。第一件事把常用算法的模板代码整理成自己的背诵版包括KMP、Dijkstra、堆排序、并查集、拓扑排序、二分图匹配、最大流模板。不要到考场上现场推大脑在紧张状态下做这些事非常容易出错。第二件事把经典模型的关键公式和适用场景做一份速查表比如KNN、K-Means、逻辑回归、BM25、朴素贝叶斯考前半小时快速过一遍。第三件事提前了解公司的主营业务和算法落地场景这样遇到场景设计题时能往对方的业务上靠回答会显得很“懂”。但也要提醒一句笔试只是秋招的第一道门槛真正决定你拿不拿得到offer的是后续的面试。笔试成绩决定了你能不能进入面试环节但面试里还会深度考察你的代码能力、项目经历和算法思维。所以不要因为笔试准备充分就放松面试环节的项目复盘和手撕代码同样需要花大力气准备。我个人在刷完这套B卷之后最大的感受是途虎的算法笔试其实是在筛选“既懂算法基础又懂业务落地”的人。纯粹的刷题机器不一定能拿高分但如果你能把每个算法的适用场景、复杂度、边界条件和业务背景串联起来这场考试对你来说就是一次展示知识体系的机会。秋招笔试前把经典算法的手算和模板都夯实把业务场景题的四步框架练熟你会发现发挥会稳很多。