途虎养车2023秋招算法笔试B卷考点解析与备战策略
途虎养车2023秋招算法笔试试卷B这套题我在网上看到不少人在讨论正好自己也完整做了一遍把考点、解题思路和一些踩坑的地方整理出来。不管你是正在准备秋招的应届生还是想跳槽的算法工程师这份拆解应该能帮你省下不少摸索的时间。试卷整体给我的感觉是不像某些大厂那样刻意出偏题怪题但非常讲究基本功的扎实程度和应用场景的灵活度。它覆盖了数据结构、经典算法、机器学习基础、甚至一些运筹优化相关内容和我之前预想的不太一样——很多题都贴着“汽车后市场”的业务场景在出这一点值得好好说道说道。1. 试卷整体认知与考点分布1.1 笔试试卷B的题型结构与通用命题逻辑先说说这套卷子的整体结构。途虎养车2023秋招算法笔试试卷B主要分为三个大部分选择题、编程题和综合题。选择题大概占比40%编程题占比40%综合题占20%。这个配比其实很能说明问题——它既想考察你的理论基础又要看你的代码实操能力同时还想了解你对真实业务场景的理解。选择题部分覆盖了数据结构、算法分析、机器学习基础、概率统计等方向难度梯度做得比较合理。前几道题偏基础比如时间复杂度的计算、常见排序算法的稳定性判断这部分如果你复习过《数据结构》教材基本没什么压力。中间几道题开始上强度涉及KMP算法的next数组求解、红黑树的插入调整、动态规划的状态转移方程推导这部分是对基本功的实打实检验。最后几道题出现了机器学习和最优化方法的内容像是SVM的核函数选择、梯度下降的收敛性分析显然是把算法工程师的岗位要求前置到笔试环节了。编程题一共三道按照惯例是两道中等难度加一道偏难的综合题。三道题都要求自己在本地IDE里写代码然后提交环境支持Java、C、Python三种语言。时间限制方面每道题给的时间还算宽裕但如果你对某个算法不熟悉现场推导也是很费时间的所以平时积累很重要。综合题这次给了一个很有意思的场景针对途虎养车的门店选址和技师排班问题要求设计一个算法方案。这类题目其实非常考验候选人的系统设计能力和业务理解深度不是说背几道算法题就能应付的。1.2 算法考点权重分析哪些是绝对重点把整张卷子的考点拉出来过一遍我整理了一张权重表方便大家对照复习考点分类具体内容出现形式权重占比基础数据结构数组、链表、栈、队列、哈希表选择题 编程题25%树与图二叉树遍历、二叉搜索树、图的最短路径选择题 编程题20%字符串算法KMP、字典树、字符串哈希选择题 编程题15%动态规划与贪心背包问题、区间DP、贪心策略证明选择题 编程题20%排序与查找快排、归并、二分查找变种选择题10%机器学习基础模型评估、损失函数、优化算法选择题 综合题10%从这个表可以看出来动态规划和字符串算法是绝对不能忽视的重点这两块加起来占了整个卷面超过三分之一的分值。而且它们恰恰是很多同学笔试翻车的重灾区——动规的状态定义一搞错就全盘皆输KMP的next数组如果没理解透彻很容易在这种细节题上丢分。还有一个值得注意的情况这次试卷里出现了几道关于“粒子群算法”“模拟退火”等元启发式算法的选择题说明出题人对智能优化算法也有一定偏好。这可能是因为途虎的业务里确实存在不少组合优化问题比如最优换货路径、库存调拨策略等。2. 核心算法考点深度解析2.1 字符串与模式匹配专题KMP算法与next数组笔试选择题里有一道很经典的KMP题直接考查对模式串next数组的推导能力。题目给的是模式串 p abacaba要求求对应的next数组值。这道题表面上是考KMP实际上考的是对“最长相等前后缀”这个概念的理解程度。我直接演示一遍推导过程。约定next[i]表示模式串前i个字符组成的子串注意是前i个不是以第i个字符结尾中最长相等前后缀的长度。对于abacabanext[1]只考虑a没有真前后缀规定为0next[2]考虑ab前缀有a后缀有b不相等为0next[3]考虑aba前缀a等于后缀a长度1前缀ab不等于后缀ba所以最长相等前后缀为1next[4]考虑abac前缀a不等于后缀c长度2的前缀ab不等于后缀ac为0next[5]考虑abaca前缀a等于后缀a长度为1长度2的前缀ab不等于后缀ca长度为3的前缀aba不等于后缀aca所以最长相等前后缀为1next[6]考虑abacab前缀ab等于后缀ab长度2长度3的前缀aba不等于后缀cab所以为2next[7]考虑abacaba前缀aba等于后缀aba长度3长度4的前缀abac不等于后缀caba所以最长相等前后缀为3所以结果就是0 0 1 0 1 2 3。这题考得其实不算偏但很多同学栽在next数组的定义上——不同教材对next[i]的定义有细微差别有的定义成“包括第i个字符的最长相等前后缀长度减一”有的干脆把next和失配函数搞混了。考试的时候一定要先看清楚题目给的公式再动手算。代码层面的KMP其实也不难关键在于失配时的回退逻辑。我用Python写了一个标准的KMP匹配流程def build_next(pattern): m len(pattern) nxt [0] * m j 0 for i in range(1, m): while j 0 and pattern[i] ! pattern[j]: j nxt[j - 1] if pattern[i] pattern[j]: j 1 nxt[i] j return nxt def kmp_search(text, pattern): nxt build_next(pattern) j 0 for i in range(len(text)): while j 0 and text[i] ! pattern[j]: j nxt[j - 1] if text[i] pattern[j]: j 1 if j len(pattern): return i - j 1 return -1实际笔试中KMP很少会直接让你写完整实现更多是像这套卷子一样以选择题的形式考next数组推导或者在编程题中需要用它做字符串匹配的优化还有一种情况是在综合题里作为子模块出现。不管哪种形式把“最长相等前后缀”的推导逻辑吃透比死记硬背代码更关键。2.2 动态规划与贪心策略从基础到场景应用这套试卷B里动态规划的分量相当足。选择题考了01背包的状态转移方程变体、最长递增子序列的时间复杂度分析编程题里有一道区间调度类型的问题需要你判断是应该用贪心还是动规。这块我是这么看的途虎的业务里无论是保养套餐的推荐、配件库存的补货策略还是技师任务的最优分配本质上都可以抽象成各种约束条件下的最优化问题所以动规和贪心在笔试试卷中占比高是合理的。先讲背包问题。选择题里有一道是这样的给定一个容量为W的背包和n个物品每个物品有重量wi和价值vi要求恰好装满背包时的最大价值如果无法恰好装满则输出0。这道题和标准01背包的区别在于“恰好装满”这个限制条件。常规的01背包初始化是dp[0] 0其他dp[j] 0表示不要求恰好装满时所有容量都有合法解但要求恰好装满时初始化要改成dp[0] 0dp[j] -infj 0这样只有从容量0开始一步步转移过来的状态才是合法解。这个细节如果不注意非常容易出问题。再看区间调度。题目大概描述是有n个技师每个技师在一个时间段内可以服务一个客户给定每个客户的服务时间段和服务收益问如何安排能使总收益最大。第一眼看上去像经典的“活动安排”问题用贪心按结束时间排序就能求最多活动数量但这里每个客户有不同的收益值就不再是简单的贪心能解决的了。正确思路是按结束时间排序后做动态规划dp[i]表示前i个客户能获得的最大收益状态转移时二分查找最近一个不冲突的客户位置。如果你能识别出“带权重的区间调度”这个模型代码写起来就很快。2.3 机器学习与智能优化算法不能忽视的送分题说实话这套试卷B里机器学习相关的题目难度不大但覆盖面很广。有考KNN算法“三个核心要素”的选择题选项分别是距离度量、K值选择和分类决策规则这个只要基础扎实就是纯送分。还有一道是关于聚类算法中K-Means和DBSCAN的对比这个常考。还有一道题是关于梯度下降的问在损失函数非凸的情况下不同的初始化参数会不会影响最终收敛结果。这里我要多说一句很多走算法岗的同学会陷入一个误区整天刷LeetCode觉得笔试就是考数据结构和算法机器学习只是面试时才需要。但实际上像途虎这样的公司算法笔试中多少都会掺一些机器学习基础题因为岗位JD里明确写了“熟悉常用机器学习算法”是加分项甚至必选项。你不一定要把SVM的数学推导背到滚瓜烂熟但至少要知道前向传播、反向传播、损失函数的概念以及常见模型适用的场景。至于粒子群算法、模拟退火这类元启发式算法试卷里以选择题出现难度不大基本考的是核心思想和应用场景的匹配。如果你没专门复习过也不要慌这类题通常可以通过排除法获得正确答案。但对这类优化算法稍微有点了解在综合题和后续面试中确实会有优势比如门店选址就可以理解成一个组合优化问题用粒子群或遗传算法来做全局搜索。3. 编程题实战拆解与代码实现3.1 高频题型一数组与双指针的灵活运用编程题第一道是比较经典的数组类问题题目背景简化成给定一个整数数组要求找出所有满足a b c 0的三元组且不允许重复。这是LeetCode上的15题“三数之和”但它的原型可以追溯到更基础的“两数之和”。这道题我展开讲一下因为它很能反映笔试题的选型逻辑。暴力做法是三重循环枚举所有组合时间复杂度O(n³)显然不行。更好的思路是先排序然后固定一个数用双指针在剩余区间内查找目标值。但这里有个关键点——怎么去重很多同学死记硬背了双指针模板但没理解去重的边界条件结果提交了一堆重复三元组白白丢分。def three_sum(nums): nums.sort() n len(nums) res [] for i in range(n - 2): if i 0 and nums[i] nums[i - 1]: continue left, right i 1, n - 1 target -nums[i] while left right: total nums[left] nums[right] if total target: res.append([nums[i], nums[left], nums[right]]) while left right and nums[left] nums[left 1]: left 1 while left right and nums[right] nums[right - 1]: right - 1 left 1 right - 1 elif total target: left 1 else: right - 1 return res为什么要先跳过重复元素再移动双针因为排序之后相同的元素会聚在一起如果你不跳过第一个数相同的情况下后两个数即使不同整体三元组也必然重复。这个细节我在直播里强调过很多次面试官也爱在这个点上深挖你如果能解释清楚会显得思路非常清晰。3.2 高频题型二二叉树遍历与路径类问题第二道编程题是二叉树的题目大概是给定一棵二叉树和一个目标值sum判断是否存在一条从根节点到叶子节点的路径使得路径上所有节点值的和等于sum。这道题就是LeetCode的112题“路径总和”解法很简单递归判断即可。但这道题有意思的地方在于它的后续追问会逐步升级从判断是否存在到要求返回所有满足条件的路径再到要求输出最大路径和。实际笔试中出题人通常不会只是简单考一个最基本的版本往往会在题面上加一些额外的限制条件比如树中可能出现负值比如要求路径不一定从根节点开始。我建议复习时把“二叉树路径”这类题当成一个系列来刷从“路径总和”到“路径总和II”再到“二叉树中的最大路径和”一口气吃透。它们的核心都是递归遍历时维护一个当前状态的累计值区别只在于在哪个时机判断结果、如何回传状态。3.3 综合压轴题业务场景下的算法建模最后一道编程题或者说综合题这次背景是“技师排班与客户预约分配”。题目给了一个简化模型有n个技师和m个预约单每个预约单有一个预计服务时长每个技师有当天可工作总时长上限请问最多能完成多少个预约单。如果只看到“最多能完成多少个”完全可以用贪心解决按服务时长从小到大排序优先安排时长最短的预约。但题目加了限制每个预约单对应一个服务的门店技师只能服务本门店的预约单。这样一来问题就从单维度贪心变成了带约束的资源分配需要按门店分组后分别处理。这种题目考的不是某个特定算法而是把现实问题抽象成算法模型的能力。我的建议是先把约束条件列出来把问题简化成自己熟悉的模型——这里每个门店独立本质上是一个个独立的“按工时上限做任务选择”的子问题每个子问题用贪心即可但如果不同门店之间允许技师支援就变成了带转移成本的调度问题复杂度就上升了。做题时优先解决自己确定的版本再说明扩展思路。4. 常见笔试陷阱与排查思路4.1 编译环境与输入输出细节在牛客网这类平台做笔试时经常遇到本机跑得好好的一提交就报错的情况。这套试卷B同样如此很多同学在选择题上没丢分反而在编程题的输入输出上栽了跟头。最常见的问题是死等一行输入却忽略多行的情况。比如部分题目会先给一个数字表示测试用例组数然后每一组数据占一行或两行。如果没看清格式直接用input().split()处理一行数据大概率会处理漏掉或者合并错误。我的习惯是写一个本地调试用的输入模板用sys.stdin.read()一次性读取所有内容然后按规则切分。这在牛客网真题、在线OJ上都比较稳。还有一个细节是Python版本。有些平台默认用Python2但绝大多数企业笔试用的是Python3写法上有区别比如/在Python2中是整除、Python3中是真除法。做题之前先确认环境如果发现身边没有环境信息就尽量用兼容的写法比如用//代替整除用print()带括号。4.2 复杂度估算与超时优化每次笔试总有几道题不是不会做而是超时了。这套试卷B的综合题部分如果直接写暴力解法数据量大的情况下可能运行超时。因此在动手写代码前先估算一下复杂度。一般经验是10的6次方以下的数据量O(n²)勉强能过10的7次方以上就必须做到O(n log n)或O(n)。如果题目的数据范围没给那就尽量给最优解法。比如上面提到的三数之和排序加双指针是O(n²)但如果你用了三重循环同样数据量下绝对超时。4.3 代码提交与注意清单我在实际踩坑中总结了一个提交前检查清单分享给大家检查是否清空局部变量。Python里容易出现全局变量和局部变量名字冲突的问题尤其是递归时。检查边界条件。空数组、长度为1的数组、树只有一个节点、全部为负数的数组——这类极端情况一定要在本地先跑一遍。检查输出格式。有的题目对结尾空格和换行很敏感虽然一般不报格式错误但影响调试判断。检查递归深度。如果深度超过1000Python默认的递归限制会报错需要手动sys.setrecursionlimit(10000)。5. 实战备考策略与时间规划5.1 考前一周的冲刺重点如果你距离笔试还有一周我建议按以下的优先级来分配时间和精力。第一优先级是高频算法模板的熟练默写包括快速排序、归并排序、二分查找、二叉树的前中后序遍历、BFS/DFS、01背包模板、完全背包模板这些应该做到闭着眼都能写出来。第二优先级是字符串算法重点是KMP、字典树、字符串哈希。第三优先级是图论算法里最常考的Dijkstra、并查集、拓扑排序、最小生成树。后面才是其他冷门算法。很多人考前喜欢刷难题我觉得没必要。笔试题的难度上限基本就是“中等题偏上一点”很少出现ICPC级别的压轴题。把基础模板掌握好、把简单题和中等题做对笔试分数已经相当可观了。5.2 简历与笔试的配合讲一个很多人不重视的点笔试之前最好重新审视一下简历里的技术栈和项目描述。为什么这么说因为部分公司的笔试系统尤其是客观题部分会自动关联简历上的技术方向。比如你在简历里写了熟悉机器学习、熟悉推荐系统选择题里可能会有更多机器学习相关内容。简历里写熟悉C编程题默认语言可能会推荐C。这些虽然不直接影响判分但整体节奏上会有影响。更重要的是综合题往往希望候选人能结合个人经历来回答。比如试卷B最后的门店选址与排班方案如果你在简历上有过类似的物流调度、路径优化项目答题时多往自己做过的项目方向引申会更有说服力。5.3 笔试后的复盘方法笔试结束不代表整个流程完事当天晚上趁热打铁复盘才是收获最大的环节。我对自己的要求是每道编程题无论做没做出来都要重新写一遍或用至少两种解法写一遍选择题里做错的题整理到错题本里并注明错误原因和涉及的考点。按我的统计算法笔试的高频错因集中在“题意理解偏差”“边界条件遗漏”“算法选型错误”三类。每复盘一道题都把错因归档下次笔试前专门翻一遍错题本比盲目刷100道新题都管用。我试过用思维导图把数据结构、算法、机器学习基础三个方向的知识点画成一张大图笔试前扫一遍效率很高。这张图里每个知识点旁边标注常考题型和常见复杂度看上去一目了然。如果你还没建立自己的知识体系可以从这套试卷B的考点分布表开始自己动手画一张收获会很大。6. 从笔试题反推岗位考察逻辑整套试卷B做下来最深的感受是出题人非常清楚自己要招什么样的人。它不追求“一题定胜负”的刁钻题而是在你熟悉的算法模板之上叠加业务场景的变化检验你是不是真的理解算法本质而不只是背了几套模板。比如那道带权重的区间调度题它的本质是动态规划但如果你被“最多能完成几个”的字眼骗了直接上贪心那就踩进了出题人设好的陷阱。这种题在LeetCode上是比较容易看穿套路的因为题号都标好了、对应解法也被总结了很多。可一旦套上“技师排班”“客户预约”的壳子很多人的思维就不再那么灵活了。这其实反映了途虎这类产业互联网公司的真实需求他们需要的不只是会刷题的人更是能把业务问题抽象成数学问题、然后落地成代码的工程师。门店选址、库存管理、智能推荐、车辆故障诊断每一个场景背后都需要算法能力做支撑。你在笔试中展现出的问题建模和分析能力往往比死记硬背某个冷门算法更有价值。另外值得注意的是最近两年很多公司的算法笔试都开始加入一些智能优化相关的内容粒子群、模拟退火、遗传算法这些元启发式方法出现的频率明显提高。这次试卷B的选择题里就出现了粒子群算法的核心原理选项。这背后的逻辑不难理解很多实际工程问题——例如配送路径规划、车辆调度优化——都是NP难问题传统精确算法在可接受时间内给不出最优解工程上可以接受次优解所以智能优化算法找到了用武之地。如果你有时间把粒子群、模拟退火、遗传算法这“老三样”的基本流程和适用场景过一遍性价比极高。最后分享一个我在做题过程中觉得特别有用的习惯每道题写代码之前先在注释里写清楚“输入是什么”“输出是什么”“约束条件是什么”“该用什么算法”先想清楚再动手。很多人笔试时一看到熟悉的题就条件反射地开始写代码反而没注意题目里微小的变化结果写完了才发现理解错了。笔试考的是准确度和速度的平衡准确度永远排在前面。