途虎养车后端秋招笔试复盘:考点拆解与编程题思路
一场秋招笔试往往比面试更能暴露一个公司的技术底色。去年九月初我参加了途虎养车2023届秋招后端开发岗的在线笔试前后一个半小时客观题加三道编程题做完的感受是这家公司的后端考察方向非常“业务导向”不堆砌偏题怪题但特别看重基础功底和用代码解决实际问题的能力。这篇文章就把整场笔试的完整复盘写出来包括题型分布、每道题的考点拆解、编程题的解题思路和参考代码以及我后来反推出来的一些备考经验。准备投递途虎或者其他互联网公司后端的同学可以拿这份复盘当一份“实战地图”参考。1. 笔试前的节奏从投简历到坐在笔试界面的48小时先交代一下背景。途虎养车的秋招启动时间不算最早八月底开网申九月初就发笔试通知。我当时是在官网投的简历投递的是上海总部的后端开发岗投完大概一周收到了笔试邮件。笔试用的是牛客网的系统双机位监考手机放侧面电脑开摄像头总时长90分钟。题型分为两部分第一部分是客观题40道左右涵盖Java基础、数据库、操作系统、计算机网络、中间件和少量业务场景题第二部分是编程题一共3道难度从签到题到中等偏上递增。这里先说一个很多人容易忽略的点笔试通知邮件里通常会写“请提前调试摄像头并完成设备检测”但我见过不少同学到开考前十分钟才登进系统结果摄像头权限没开、浏览器不兼容白白浪费了宝贵的答题时间。我当时提前一天就把牛客网的笔试环境测了一遍开考前半小时登录等待这个习惯后来也帮我避免了很多麻烦。另外建议准备一张草稿纸和一支笔。客观题里有一些涉及SQL执行顺序、JVM内存分配的选择题光靠脑子推容易乱写下来会稳很多。编程题的题目描述通常比较长我习惯先把关键条件画出来避免漏看边界条件。整体来说这套笔试题的难度属于“中等偏上但不劝退”的水平。客观题里没有那种故意刁难人的脑筋急转弯但有几个题需要你真正理解原理才能选对死记硬背反而容易掉坑。编程题第一题基本是送分题第二题需要一点数据结构功底第三题则明显在筛选算法能力比较强的候选人。2. 客观题拆解Java基础、数据库与中间件的考察重点客观题部分覆盖的知识面比较广但仔细分析下来每个方向的出题侧重点其实很清晰。我把每一类题目的高频考点和踩坑点分开说。2.1 Java集合与并发HashMap和ConcurrentHashMap是常客Java集合这块几乎是必考的途虎的题也不例外。我遇到的题目里HashMap的底层原理、ConcurrentHashMap的分段锁机制、ArrayList和LinkedList的适用场景都出现了。特别是ConcurrentHashMap题目问的是JDK1.8之后它如何保证线程安全选项里混着“分段锁”“CASsynchronized”“全局锁”“读写锁”这几个说法如果只看过八股文没真正看过源码很容易选成分段锁——那是1.7的旧方案1.8已经改成CAS配合synchronized锁住桶首节点了。并发这块还考了synchronized和ReentrantLock的区别、volatile的可见性和禁止重排语义、线程池的核心参数和执行流程。有一道题给了核心线程数2、最大线程数5、阻塞队列容量3的场景问提交8个任务时线程池如何响应。这类题只要记住那个执行流程先判断核心线程是否已满没满就创建线程执行满了丢队列队列满了才创建非核心线程都满了走拒绝策略。按这个顺序推答案基本不会错。我的建议是准备这类题目不要只背结论要把“为什么”搞清楚。比如volatile为什么不能保证原子性、synchronized为什么是重量级锁但JDK1.6之后又引入了偏向锁和轻量级锁这些都是面试官层层追问的素材笔试选择题也常常把原理和结论混在一起考。2.2 JVM与内存模型考得深但不偏JVM相关的题目大概有四五道涉及堆内存分区、GC Roots、常见的垃圾收集器、类加载的双亲委派模型。有一道题让我印象很深给出一段代码问某个对象在什么时候会被回收选项涉及可达性分析、引用计数、finalize方法执行时机。这题考的是“可达性分析算法”和“finalize自救”这两个知识点如果只看过概念没看过《深入理解Java虚拟机》里的例子很容易觉得finalize一定会执行但实际上一个对象的finalize方法只会被系统调用一次而且不保证执行完成。途虎在JVM这块没有考非常冷门的东西比如G1的Region布局、ZGC的染色指针这些都没出现。但这不代表可以不准备相反基础的堆栈分区、GC算法对比、类加载过程这些一定要滚瓜烂熟。我复习时习惯把“JVM运行时数据区”画成一张图把每个区域存什么、会不会OOM、GC是否涉及标注清楚这样遇到选择题一眼就能定位考点。2.3 MySQL索引与事务结合业务场景的必考题数据库这块途虎考得比较实在基本上都是后端日常开发中最常用的知识点。索引部分考了最左前缀法则、覆盖索引、索引失效的场景。有一道题给了几个SQL查询条件问哪个查询能用上联合索引(a,b,c)选项里分别有只查a、只查b、查a和c、查a和b和c。这个只要记住“最左前缀”的匹配顺序就能做对但要注意“查a和c”这种情况——a能用索引c用不了因为中间跳过了b所以是部分失效。事务隔离级别也是必考项。题目给了四种隔离级别下的典型问题问哪种隔离级别解决了幻读。这里有个坑MySQL的InnoDB引擎在可重复读隔离级别下通过MVCC间隙锁解决了幻读问题所以答案不是“串行化”而是“可重复读”。这一点和标准SQL的定义不同如果死记硬背教材上的结论反而会选错。业务场景题里有一道是一张订单表数据量过千万查询某个用户最近三个月的订单时非常慢问以下哪个方案最优。选项有“在订单号上加索引”“在用户ID和下单时间上加联合索引”“分库分表”“加缓存”。虽然分库分表看起来很高端但对于这种查询场景最优解其实是建联合索引。笔试选择题里这类题常考的就是“先看索引能不能解决再看要不要上中间件”不要一上来就选分库分表这种重方案。2.4 Redis与消息队列中间件考察的风向标途虎笔试里Redis出现得不少毕竟汽车后市场的业务场景里门店库存、优惠券、用户会话这些都很依赖缓存。考了缓存穿透、缓存击穿、缓存雪崩的区别和应对方案Redis的数据类型和底层结构还有过期删除策略。有一道题是问“缓存穿透”的解决方式选项里有布隆过滤器、设置过期时间、互斥锁、限流。这里要注意设置过期时间是解决不了穿透的穿透是“查询一个不存在的key”每次都会打到数据库布隆过滤器或者缓存空值才是常见的解法。消息队列考得比较基础主要是Kafka和RocketMQ的使用场景、消息不丢失的机制、消费组的原理。有一道题问“如何保证消息不重复消费”这题不是考单个组件而是考“消费幂等性”的通用设计比如用唯一业务ID去重、数据库唯一约束、Redis分布式锁等等。这类题没有标准答案选最通用的方案就好。我的感受是途虎对中间件的考察还是偏“使用和理解层面”不会让你手写一个Raft协议或者分析Kafka的日志段结构。但如果你简历上写了熟悉Redis或者熟悉消息队列笔试时这类题就必须拿满分因为后面面试一定会追问。2.5 计算机网络与操作系统老四样依然稳定计网和操作系统各占了几道题。计网这边考了TCP三次握手、四次挥手的状态变化、HTTP和HTTPS的区别、TCP和UDP的典型应用场景。有一道题给了几个网络故障现象问可能是哪一层出了问题这种题需要结合DHCP、DNS、TCP连接等知识综合判断。操作系统考了进程和线程的区别、死锁产生的四个必要条件、虚拟内存和页面置换算法。有一道题是问“发生死锁的必要条件不包括哪一个”选项有互斥、占有并等待、不可剥夺、循环等待答案是“循环等待”其实是被推导出来的结果不是必要条件本身——严格来说“循环等待”是前面三个条件同时满足时的表现。这类题就属于“你以为你会了但其实细节没抠到位”复习时要注意概念的严谨边界。3. 三道编程题从签到题到优化题的完整复盘编程题是整套笔试里最拉分的部分三道题总分值约占总成绩的一半。途虎的编程题风格偏“业务场景算法模型”每道题都包装了一个实际的养车场景但内核还是经典算法。我把三道题的题目还原、解题思路和参考代码逐一写出来。3.1 第一题保养记录统计前缀和/差分题目大意是给定一组车辆的保养记录每条记录包含保养日期和保养门店ID现在给出若干个查询每个查询给一个日期区间要求统计区间内每个门店分别产生了多少条保养记录。这道题本质上是一个区间统计问题。数据范围不大时最直接的做法是遍历记录逐个统计区间内的数据时间复杂度是O(N*M)N是记录数M是查询数当N和M都到10^5级别时会超时。优化思路是前缀和先按门店ID为每个门店分别维护一个按日期排序的记录数组再预处理每个数组的前缀和。查询某个门店在某个日期区间内的记录数时用两个二分查找定位左右边界然后用前缀和相减得到结果。这样单次查询的时间复杂度降到了O(log K)K是单个门店的记录数。我当时用的Java实现大致是这样的public class Main { // 假设 records 已经按门店分组每个门店的记录日期升序存储 // map.get(storeId) 返回该门店的日期数组 public static int query(ListInteger dates, int left, int right) { // 二分找第一个 left 的位置 int l lowerBound(dates, left); // 二分找最后一个 right 的位置等价于找第一个 right 的位置减一 int r upperBound(dates, right); return r - l; } private static int lowerBound(ListInteger arr, int target) { int lo 0, hi arr.size(); while (lo hi) { int mid (lo hi) 1; if (arr.get(mid) target) { lo mid 1; } else { hi mid; } } return lo; } private static int upperBound(ListInteger arr, int target) { int lo 0, hi arr.size(); while (lo hi) { int mid (lo hi) 1; if (arr.get(mid) target) { lo mid 1; } else { hi mid; } } return lo; } }这里有一个笔试中常见的坑如果你没有按门店分组而是尝试在查询时用遍历所有记录的方式统计在小数据量下能通过部分用例但大数据量时就会超时。另外如果题目给出的日期是字符串格式比如“2023-08-01”比较时要统一转成整数或直接用字符串比较不要混用格式。3.2 第二题门店订单滑动窗口滑动窗口/单调队列题目大意是有一家门店连续N天的订单量数组给定一个窗口大小K要求输出每个长度为K的连续子数组中的最大订单量。也就是经典的“滑动窗口最大值”问题。这道题我第一时间想到的是优先队列大顶堆方案维护一个K大小的窗口每次移动时新元素入堆同时把不在窗口内的堆顶元素弹出堆顶就是当前窗口的最大值。时间复杂度是O(N log K)。但更优的解法是单调队列用双端队列维护窗口内元素的下标保证队列中的下标对应元素是递减的。这样队首始终是当前窗口的最大值每个元素最多入队出队各一次时间复杂度O(N)。笔试时如果时间充裕我会优先写单调队列方案因为代码同样不复杂但复杂度更优能体现出算法功底。参考实现public class Main { public static int[] maxSlidingWindow(int[] nums, int k) { int n nums.length; int[] ans new int[n - k 1]; DequeInteger deque new ArrayDeque(); for (int i 0; i n; i) { // 移除窗口外的元素 while (!deque.isEmpty() deque.peekFirst() i - k) { deque.pollFirst(); } // 维护单调性队尾元素小于等于当前值时直接弹出 while (!deque.isEmpty() nums[deque.peekLast()] nums[i]) { deque.pollLast(); } deque.offerLast(i); // 窗口形成后记录答案 if (i k - 1) { ans[i - k 1] nums[deque.peekFirst()]; } } return ans; } }这道题在笔试里属于“基础数据结构应用题”难度不大但很多人会卡在边界条件上比如K等于1时、K等于N时以及窗口刚开始滑动还没完全进入数组时——前面几个窗口是没有完整答案的要特别注意下标计算。我写题时会把边界条件先列出来再写代码这种习惯能明显降低失误率。3.3 第三题优惠券组合优化动态规划/贪心题目大意是平台发放多张优惠券每张优惠券有两个属性面额和满减门槛即消费满X元才能用Y元券。用户购买一批商品总价固定但需要拆分成多个订单来凑不同的满减门槛。问如何分组商品使总优惠金额最大。这道题的综合度明显比前两道高需要先理解问题本质。每个订单必须满足某张优惠券的门槛才能使用且每个订单最多用一张券。不同商品的单价不同拆分订单时还要考虑商品不能拆分一个商品只能放进一个订单。我当时的思路是先忽略“商品不可拆分”这个约束假设可以任意切分金额这个问题就退化成“贪心选券”的场景——优先用门槛低、面额高的券。但加了商品不可拆分的约束后就变成了一个分组问题需要动态规划。我用的状态定义是dp[i]表示前i个商品能获得的最大优惠金额但这里有个麻烦——商品要分组组的总金额要满足某张券的门槛。所以本质上是一个“集合划分”问题复杂度可能很高。笔试时间有限我采用了近似解法先把所有可能的订单组合按总金额排序优先处理“门槛满足且优惠/门槛比最高”的券再把能组合的商品凑在一起。这种贪心策略虽然不能保证全局最优但在数据范围不大时能拿到大部分分数。如果是刷题阶段这一题更推荐用DFS枚举所有商品分组方式再用记忆化搜索优化因为这是一个典型的“子集枚举背包价值计算”模型。笔试时如果数据范围是10^5级别这道题更可能考察排序后贪心加双指针而不是真正的NP问题。我当时在草稿纸上分析了一下面额与门槛的关系发现数据有明显规律——面额越大的券门槛越高且不存在相互覆盖的情况所以贪心就是最优解。这类“题目里藏着简化条件”的情况在笔试中非常常见不要一上来就套复杂算法先读清楚约束条件。4. 从笔试反推途虎后端技术栈与岗位画像一套好的笔试题其实是一面镜子能映出这个团队的技术偏好和业务重点。考完途虎这套笔试题我能明显感觉到几个信号。4.1 考察内容折射出的技术栈从客观题里出现的知识点来看途虎后端的技术栈大概率以Java为主Spring Boot是基础框架数据库层面用MySQL缓存用Redis消息队列在Kafka和RocketMQ之间二选一或都有使用。这套组合在国内互联网公司里属于“标准件”意味着他们招人时不会预设你必须会某种冷门框架但要求你把主流技术栈的底层原理吃透。另外客观题里多次出现分布式的通用问题比如幂等性、分布式锁、缓存一致性。这类题目往往不是某个中间件独占的考题而是考察你是否具备“分布式思维”——知道一个请求经过网关、服务、缓存、数据库的全链路中哪些地方会出现数据不一致、哪些地方需要幂等保护。这种能力很难速成需要真正做过项目、踩过线上坑才能答得稳。4.2 业务场景题背后的岗位画像途虎养车的核心业务是汽车后市场涉及门店、仓储、物流、订单、优惠券、用户增长等多个业务域。笔试里出现的保养记录统计、门店订单窗口、优惠券组合其实都是实际业务中会遇到的真实问题。这说明这个岗位不是纯算法岗而是“业务研发”属性更强的后端岗——你写的代码要直接服务于业务逻辑而不是做抽象的算法模型。如果你也在投递类似的业务型后端岗位我的建议是简历里的项目经历一定要有业务场景感光写“实现了用户登录注册”这种是远远不够的。你要能讲清楚你的项目解决了什么业务问题、你在其中负责哪个模块、遇到的最大技术挑战是什么、如何解决的。这次笔试里有一道客观题就是给一个具体的订单超卖场景让选择最优的解决方案我一看就知道出题人希望候选人具备实战经验而不是只会在评论区背“乐观锁vs悲观锁”的口诀。4.3 途虎笔试与通用大厂笔试的差异对比我同期做的其他几家互联网公司的笔试途虎这套题的“业务味道”更浓。有些大厂的笔试题几乎全是纯算法题从二叉树到图论到动态规划轮番上阵和实际工作内容脱节严重。途虎虽然也有三道编程题但题干都包装了实际的养车业务场景客观题里也出现了不少业务场景题。这意味着如果你平时喜欢刷LeetCode但没怎么做过真实项目做纯算法大厂的笔试可能如鱼得水但做途虎这种业务导向的笔试反而会吃亏。反过来如果你项目经验丰富但算法基础一般途虎的题目友好度会更高一些。当然这只是一个相对判断编程题该难还是难第三道优惠券题就刷掉了一批算法功底不扎实的人。5. 踩坑复盘与给后续求职者的备考清单最后聊点实在的把我这次笔试踩过的坑和后来总结出的备考思路整理出来给准备秋招的同学做个参考。5.1 我踩过的三个坑第一个坑是客观题时间分配不合理。我前30分钟把精力放在了每道客观题上遇到拿不准的题反复纠结结果到编程题时只剩下不到40分钟。涂改选择题的答案风险很大——笔试系统不支持返回查看已提交的客观题一旦提交就没办法修改。我的建议是客观题平均每题控制在40秒以内遇到完全没思路的题先标记后跳过优先保证编程题有充足时间。第二个坑是编程题第一题我一开始用了最暴力的遍历解法虽然思路简单但提交后只通过了30%的测试用例剩下的大数据量用例全部超时。后来才改成前缀和二分。这提醒我拿到题目后不要急着写代码先在草稿纸上分析数据范围预估暴力解法能不能过如果明显会超时就提前思考更优的算法。第三个坑是环境问题。牛客网的编程题支持多种语言我选的Java但本地IDE写代码时有代码提示笔试系统的编辑器是纯文本没有自动补全导致我写Deque的API时记不清是pollFirst还是removeFirst浪费了好几分钟。备考阶段一定要在牛客网的模拟环境里练几次手写代码特别是Java集合类的API别在这种细节上翻车。5.2 秋招后端笔试的通用备考清单结合途虎这次笔试和其他几家公司的经验我整理了一份应对秋招后端笔试的备考清单按优先级排序算法基础数组、链表、栈、队列、哈希表、二叉树、图、前缀和、差分、滑动窗口、双指针、二分查找、贪心、动态规划背包、LIS、区间DP、并查集、字典树。这些是笔试编程题的高频考点每天保持刷2-3道题的节奏即可。Java基础集合源码HashMap、ArrayList、LinkedList、ConcurrentHashMap、并发编程synchronized、Lock、volatile、线程池、AQS、JVM内存模型、GC、类加载、调优参数。数据库索引原理、SQL执行顺序、事务隔离级别、MVCC、锁机制、慢查询优化。最好能自己动手explain分析几条SQL。中间件Redis的数据结构、持久化、过期策略、缓存穿透/击穿/雪崩、分布式锁消息队列的基本概念和消息不丢失、幂等性保障。计算机网络与操作系统TCP/UDP、HTTP/HTTPS、三次握手四次挥手、进程线程、死锁、虚拟内存、页面置换算法。项目经验至少准备两个有深度的项目能用STAR法则讲清楚业务背景、技术方案、难点和成果。5.3 笔试之外简历与项目准备的补位策略笔试只是秋招的第一道关卡后面还有技术面试、HR面。我在准备过程中有一个很深的体会笔试考的是“知识覆盖面”面试考的是“理解深度”。有些同学笔试成绩很高但面试时一问到底就露馅因为只是背了八股文没有真正理解原理。所以我的建议是笔试的备考要和面试的深度准备同步进行。每复习一个知识点都要问自己三个问题它解决了什么问题它的核心原理是什么如果让我现场设计一个类似的东西我会怎么做比如复习Redis的缓存穿透不要只记布隆过滤器这个名词要知道布隆过滤器的位数组怎么设计、哈希函数怎么选、误判率如何计算、如何删除元素。这样即使面试官层层追问你也能接得住。另外项目这块一定要提前打磨。秋招时间线很紧不要等到笔试过了才开始准备项目介绍。我建议在投简历之前就把项目的架构图、核心表结构、关键接口的时序图画好这样笔试当天间隙也能快速温习面试前直接拿来讲就行。我自己在准备过程中发现把每个项目的“难点”总结成三句话特别有用一句话说清楚业务背景一句话说清楚技术难点一句话说清楚解决方案和效果。面试官问项目时先用这三句话把主线立起来再根据追问展开细节效果远比从头到尾流水账式地介绍项目要好。秋招是一场持久战笔试只是其中一环。途虎这套题给我的整体感受是只要基础扎实、算法手感在线、有真实的业务项目经验拿到面试机会并不难。希望这份复盘能帮你少踩一些坑祝你顺利上岸。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →