猿辅导2017校招下午场面经:算法与Java核心考点全复盘

发布时间:2026/8/31 16:55:28
猿辅导2017校招下午场面经:算法与Java核心考点全复盘 2017年秋招那会儿猿辅导还没有现在这么家喻户晓但在北京应届生圈子里它家的校招面试已经以“题硬、环节多、流程紧凑”出名了。我当时预约的就是下午场从下午两点半进场一直到六点多才出来三轮技术面加一轮HR面脑子到后面完全是靠着惯性在跑。网上关于猿辅导2017校招上午场、下午场的面经帖其实不少但大多是零散的“题目回忆”很少有把整场面试的节奏、考点、答题思路和踩坑点串起来讲的。所以今天这篇我打算把自己作为一个亲历者后来又站在面试官角度复盘时总结出来的东西一次性整理清楚。先交代一下内容边界时间已经过去好几年具体原题不可能一字不差但我尽量保留2017年下午场面试的考点逻辑和考察方向。这篇文章主要写给正在准备算法岗、后端研发岗校招的同学也适合那些想知道“公司下午场面试到底会怎么考”的朋友。所有题目的还原都来自回忆和同类场景归纳重点不在让你背答案而是看清每道题背后真正想考什么。1. 下午场面试的整体节奏与考察框架1.1 从网申到现场猿辅导校招流程还原2017年猿辅导的校招流程基本是这样一条线网申投递 → 在线笔试 → 面试邀请 → 现场技术面 → HR面。在线笔试是第一道硬门槛当时用的网页编辑器没有本地IDE限时2到3道算法题题目难度明显高于一般互联网公司。我记得当时有一道题是“给定一个字符串找到最长回文子串”另一道是“设计一个容器支持随机访问和删除且时间复杂度为O(1)”。这种题放在笔试阶段就已经确定了它家面试的基础基调算法能力不过关后面基本走不深。到了现场面试环节下午场通常约在13:30到14:00开始候选人被统一带到一个等候区按批次叫号。下午场的等待时间往往比上午场更不可控因为上午如果有候选人聊超时下午的时间表就会整体顺延。我当时等了大概40分钟才被叫到这40分钟不建议干等我后来作为面试官也见过很多候选人在等候区用手机最后扫一眼自己的速查笔记这个习惯很好。下午场和上午场最大的区别在哪里我个人的体会是面试官连续面了一上午之后体力和注意力都会下降到下午他们已经没有太多耐心听你绕弯子。问问题的节奏会更快追问会更直接你不懂的地方能很快被探测出来。反过来这对候选人其实是一件好事只要你基础扎实、表达清楚下午场反而容易让面试官眼前一亮因为大部分人在下午的表现是打折扣的。谁能稳住状态谁就能拿到不错的评价。1.2 三轮技术面分别考察什么猿辅导2017年的研发岗面试流程一般是三轮技术面加一轮HR面三轮技术面各有侧重划分比较清晰。第一轮是基础算法与代码能力。面试官会先让你做自我介绍然后快速进入算法题常见方向是链表、树、字符串、动态规划。题目不会特别偏门但要求你当场写代码并且在写之前先口头说思路。这一轮筛掉的是“只会刷题看不懂题目”的人。第二轮是语言基础和系统知识。针对后端岗位会深挖Java集合、并发编程、JVM、数据库索引和事务。我遇到的问题是“HashMap在1.7和1.8之间有什么区别”“线程池的核心参数有哪些”以及“MySQL索引在什么情况下会失效”。这些题目看起来基础但面试官会连续追问比如你提到HashMap扩容他会接着问你“为什么扩容是2的整数次幂”“头插法和尾插法在网络环境里会有什么问题”。第三轮是综合面面试官通常是团队leader或者交叉面的资深工程师。这一轮不纯粹考知识点而是给你一个偏开放性的场景题比如“如果让你设计一个在线答题系统你会怎么考虑并发和缓存”。这轮看重的是你的知识面、系统设计意识以及面对未知问题时的拆解能力。三轮面试环环相扣上午场和下午场在题目上不一定有重叠但考察框架是稳定不变的。你要做的不是去押题而是把每一层的核心能力都练到位。1.3 面试官评分时最看重的三个维度我后来参与过几次校招面试以后发现面试官在评分表上并不会只记录“答对了哪道题”而是会从三个维度打分。第一维度是沟通的效率。你能不能在半分钟内把项目说清楚你解释算法思路时是有条理地分情况讨论还是车轱辘话来回说很多技术好但沟通差的人在这个维度上吃大亏。第二维度是问题拆解能力。现场写代码时你会不会先确认输入边界会不会主动提到空指针、重复元素、大数溢出这些特殊情况面试官通过追问边界条件来观察你是否有系统思维而不是写一个能跑通示例就结束。第三维度是基础知识的“抗追问能力”。你能说出来HashMap线程不安全只是第一步关键在于你知不知道它为什么会线程不安全、在多线程环境下会出什么问题。只背结论的人一追就露馅。所以下午场面试与其说是考察“你刷了多少题”不如说是在很短的时间里判断你有没有解决未知问题的底层能力。2. 高频考点与典型题目思路拆解2.1 算法题主攻方向链表、树、字符串、动态规划从2017年猿辅导笔试和面试的题目来看算法题基本集中在四个方向。链表类题目考的是指针操作和边界处理反转链表、合并两个有序链表、判断环形链表是最高频的三种。树类题目考的是遍历思维和递归转换的能力二叉树前中后序遍历、层序遍历、最近公共祖先、判断平衡二叉树几乎是必练清单。字符串类题目则偏重滑动窗口和双指针技巧最长回文子串、无重复字符的最长子串、字符串编辑距离都是很典型的考题。动态规划在面试中出现的概率也很高但并不是那种特别复杂的DP更多是状态定义清晰、递推公式简单的题型。我印象很深的是有一道“一个机器人从网格左上角走到右下角有多少条不同路径”的题面试官并没有让我直接写代码而是先让我解释“为什么可以用DP”然后再让我优化空间复杂度到O(n)。这种提问方式说明面试官想看的不是你会不会背模板而是能不能分析出状态依赖关系。准备这些高频方向不需要把LeetCode所有题都刷一遍。更有效的策略是按题型分类整理每种题型选10道经典题反复练习到能独立讲出思路、写出无Bug代码。我在下午场面试前一周就把链表和二叉树所有常见题重新撸了一遍面试时遇到了“判断平衡二叉树”因为已经形成肌肉记忆写得非常顺。2.2 Java高频问题从HashMap到线程池到JVM如果你是面后端岗位第二轮技术面大概率会碰到Java基础。HashMap是绝对高频2017年面试官特别喜欢问1.7和1.8的区别。你需要答出三个关键点第一1.7用头插法1.8用尾插法第二1.7底层是数组加链表1.8当链表长度超过8时会转成红黑树第三1.8的resize过程对hash冲突的rehash做了优化性能更好。但这只是开始面试官一定会追问“HashMap为什么线程不安全”你得继续说明在多线程并发put时可能出现数据覆盖、死循环等问题还要提到ConcurrentHashMap是怎么通过分段锁或者CAS加synchronized来规避的。线程池这一块考察核心参数含义和执行流程。核心线程数、最大线程数、阻塞队列、拒绝策略这四个参数要能倒背如流。尤其要理解当一个任务提交到线程池是先判断核心线程是否已满而不是直接创建新线程核心线程满了以后任务进入阻塞队列而不是直接扩容到最大线程数。这个执行顺序很多人会记反面试时答错就特别致命。JVM相关问题主要集中在内存区域划分和垃圾回收上。你需要能画出堆、栈、方法区、程序计数器、本地方法栈的划分并解释每个区域存放什么内容。GC部分要理解可达性分析算法、GC Roots有哪些以及Minor GC和Full GC的区别。我当时被问到一个问题“什么时候对象会进入老年代”我把大对象直接分配、长期存活对象晋升、动态年龄判断这三个场景都说了面试官很明显地点头了。这种“多答一个边界情况”的细节其实比答完标准定义更有价值。2.3 数据库与场景题不只是“背八股”基础题之外下午场面试还有一块让人头疼的是数据库问题。MySQL索引“最左前缀原则”几乎是必问你要能解释联合索引里的字段顺序为什么不能随便调换也要能举出索引失效的例子比如对索引列使用函数、隐式类型转换、like语句前置百分号。更深一层面试官会问你“为什么MySQL用B树而不跳表”这题考的是对数据结构的理解B树磁盘IO次数少叶子节点形成双向链表方便范围查询在数据库这种读多写少的场景下很合适。事务隔离级别也经常考读未提交、读已提交、可重复读、串行化这四级要能按照隔离强度排序。MySQL默认是可重复读这个一定要说清楚并且要知道它是通过MVCC多版本并发控制来实现快照读的。面试官可能会追加“可重复读和幻读的关系”你如果能提到Next-Key Lock解决了一部分幻读问题就已经超过大多数候选人了。场景题是下午场的重头戏之一。我记得面试官问过一个问题“如果在线考试系统里有一道题的答案要写入数据库但大批考生同时点击交卷你会怎么设计”这个问题的本质是考察高并发下的缓存和异步处理。比较合理的思路是先加一层Redis缓存把提交记录先写缓存再通过消息队列异步落库避免数据库瞬时压力。你还需要提到幂等性设计因为网络重试可能导致同一提交被写入多次。面试官要听到的不是一个标准答案而是“你会不会从不同层面去分析问题”。3. 手写代码实战三道典型题的详细解析3.1 反转链表迭代与递归两个版本都要会反转链表是我在2017年秋招面试里遇到概率最高的题库没有之一。下午场那轮技术面面试官第一道题就让我写反转链表并且明确说“先写迭代再写递归”。迭代版本的写法和原理如下。我们需要维护三个指针prev、curr、next通过不断把当前节点的next指向prev实现局部反转。代码如下public ListNode reverseList(ListNode head) { ListNode prev null; ListNode curr head; while (curr ! null) { ListNode nextTemp curr.next; curr.next prev; prev curr; curr nextTemp; } return prev; }这段代码最容易被忽略的地方是在修改curr.next之前一定要先用nextTemp保存原来的下一个节点。我当时看到不少人直接写curr.next prev结果链表断掉后面的节点全丢了。写完以后面试官让我分析复杂度你要能说出来时间O(n)空间O(1)。递归版本的核心思路是假设当前节点之后的链表已经反转完成只需要将下一个节点的next指向当前节点。代码如下public ListNode reverseList(ListNode head) { if (head null || head.next null) { return head; } ListNode newHead reverseList(head.next); head.next.next head; head.next null; return newHead; }注意递归版本的空间复杂度是O(n)因为递归调用会占用系统栈。面试时最好主动提一句“递归写法更简洁但链表很长时可能栈溢出”这会让面试官觉得你对边界和代价有意识。3.2 LRU缓存用HashMap加双向链表实现LRU缓存是面试官非常喜欢考的场景题因为它既考察基础数据结构又考察工程实现能力。2017年下午场里我第二轮的面试官就让手写一个“最近最少使用的缓存”。在Java里实现LRU缓存的简洁方案是继承LinkedHashMap重写removeEldestEntry方法。但面试官如果只看到你这么做一般会要求你继续“用HashMap加双向链表手写”来验证你真的理解底层逻辑。手写版本的核心结构是class LRUCache { class DLinkedNode { int key; int value; DLinkedNode prev; DLinkedNode next; public DLinkedNode() {} public DLinkedNode(int key, int value) { this.key key; this.value value; } } private MapInteger, DLinkedNode cache new HashMap(); private int size; private int capacity; private DLinkedNode head; private DLinkedNode tail; public LRUCache(int capacity) { this.size 0; this.capacity capacity; head new DLinkedNode(); tail new DLinkedNode(); head.next tail; tail.prev head; } public int get(int key) { DLinkedNode node cache.get(key); if (node null) { return -1; } moveToHead(node); return node.value; } public void put(int key, int value) { DLinkedNode node cache.get(key); if (node null) { DLinkedNode newNode new DLinkedNode(key, value); cache.put(key, newNode); addToHead(newNode); size; if (size capacity) { DLinkedNode tailNode removeTail(); cache.remove(tailNode.key); size--; } } else { node.value value; moveToHead(node); } } }你要能解释清楚为什么用双向链表删除任意节点时需要O(1)时间必须知道前驱节点的位置单向链表做不到。还要解释为什么HashMap保存的是key到链表节点的引用这样能在O(1)时间内找到某个key对应的链表位置。这里的核心思想是“用空间换时间用两种数据结构互相弥补”。写完之后主动说“put和get的时间复杂度都是O(1)”会给面试官留下好印象。3.3 第K大的数快速选择与堆两种思路在下午场第三轮我遇到了一道“从一个无序数组中找到第K大的数”。题目看起来简单但它背后能延伸出很多讨论。最简单的思路是把数组排序然后直接取值时间复杂度O(n log n)。但面试官一定会追问“能不能更快”。这时候你应该想到快速选择算法也就是借助快速排序的partition思想每次把数组分成左右两部分判断目标位置在哪一侧只递归处理那一侧。平均时间复杂度O(n)最坏O(n^2)。核心代码片段如下public int findKthLargest(int[] nums, int k) { int target nums.length - k; int left 0; int right nums.length - 1; while (left right) { int pivotIndex partition(nums, left, right); if (pivotIndex target) { return nums[pivotIndex]; } else if (pivotIndex target) { left pivotIndex 1; } else { right pivotIndex - 1; } } return -1; } private int partition(int[] nums, int left, int right) { int pivot nums[right]; int storeIndex left; for (int i left; i right; i) { if (nums[i] pivot) { swap(nums, i, storeIndex); storeIndex; } } swap(nums, storeIndex, right); return storeIndex; }面试时我还补充了另一种思路维护一个大小为K的最小堆遍历数组如果堆未满就加入堆满后遇到比堆顶大的数就替换堆顶最后堆顶就是第K大。时间复杂度和空间复杂度要能说清楚。为什么这道题值得好好准备因为它很考验你的算法基本功而不是单纯的记忆。如果你能主动讨论“重复元素怎么办”“内存不足时用堆更快”面试官会觉得你有工程视角。4. 现场实战中的常见失误与排查技巧4.1 沟通误区要么沉默解题要么抢跑下午场面试里最忌讳的一件事就是拿到题目后埋头写代码完全不和面试官交流。我第二次参加面试时有个候选人写了快十分钟代码一调试整体思路完全跑偏等于白写。面试官只能干坐在那里最后给的时间不够只能草草结束。正确的做法是花一到两分钟先口述思路。你可以这样说“我先把特殊情况处理掉比如链表为空或只有一个节点然后我想用双指针来做因为这样可以做到O(n)时间复杂度和O(1)空间复杂度我接下来先从例子开始推演。”这种表达比沉默代码要好非常多。但也不要走向另一个极端思路还没成型就急着说导致话里前后矛盾。比较好的节奏是“先说大致思路取得面试官确认后再写代码”。这不代表你能力不够恰恰说明你是一个懂得对齐目标和边界的人。4.2 白板书写规范代码整洁度就是印象分现场手写代码的场景有的是白板有的是纸上。很多人刷题时习惯用IDE自动补全手写时就暴露出问题。比如变量名起得随意括号对不齐逻辑分支没有用空行分隔甚至写完以后自己都读不下去。我当时给自己定了一个标准手写代码要像发布到GitHub上一样认真。变量名用有意义的词比如node、nextTemp、storeIndex不要用a、b、c。写完以后用三到五分钟在脑子里“虚拟执行”一遍走一个简单用例再检查一次边界条件。这个习惯能帮你发现很多低级错误。还有一个小技巧写代码之前先把函数签名写出来然后确认参数类型和返回值。很多人在半途才发现返回类型不对又得整段重写。先确认函数签名等于给自己定了框架后面填内容就会更顺。4.3 心态崩了怎么办一道题卡住不代表全盘结束下午场因为疲倦很容易在第三轮遇到一道看不懂的题就心态崩塌。我自己也遇到过类似情况当时第三轮面试官出了一道关于“字符串编辑距离”的题我第一反应是“完了DP没复习到”。但后来我强迫自己冷静下来先从这个问题的暴力解法开始想再尝试找子问题重叠最后虽然没有写出最完美的解但面试官看到了我推导递推公式的过程最后还是给了一个不错的评价。当你在面试时遇到不会的题最重要的不是“硬解”而是“把思考过程暴露出来”。你可以直接说“我现在没有立刻想出来最好的解法但我先分析一下输入和输出的关系”。然后尝试从最简单的例子里找规律比如长度为1、长度为2时应该返回什么。面试官一般不会因为你暂时没想到最优解就直接判负他们更在意你的思考方式是否有可能达到那个解。4.4 面试结束后必须做的复盘清单面试结束不等于这件事结束了。我个人的习惯是无论面得像不像样从公司出来以后趁记忆还新鲜立刻找一家便利店坐下来把整场面试过程中被问到的所有问题、答得好的点和卡壳的点都写进手机备忘录。复盘清单一般包含下面几项面试官问了哪些问题我分别是怎么答的。哪些问题是我模糊、不确定、没有答到点子上的。在后续复习中需要针对这些模糊点补哪些知识。面试过程中有没有出现沟通不畅的情况该怎么改进。这比单纯记题目更有价值。因为你不只是收集了题目还收集了“哪些知识点需要重学”的信号。我在2017年秋招期间就是靠这个复盘清单把所有面试中出现过的模糊知识点逐个消灭后面再面其他公司时明显越来越顺。这里再分享一个实用细节如果面试中出现了你没听懂的术语不要不懂装懂直接请面试官解释。你可以在面试结束时问一句“刚才提到的一致性哈希我理解得还不够透彻可以请教一下吗”大多数面试官会很乐意帮你解答。这不仅帮你补了一个知识点还让面试官看到一个愿意学习、不装懂的形象。根据我个人当年下午场的经验面试前状态调整也很关键。午后本来就容易犯困如果前一天熬夜刷题到两三点再赶下午场脑子基本是木的。我那时候的做法是下午有面试就提前两三天把作息调成早睡早起面试当天中午只吃七八分饱避免血液全跑到胃里然后提前半小时到楼下走一圈让大脑保持轻度活跃。最后再提醒一句所有高频题目一定要练到“闭着眼也能写出来”的程度因为现场的压力会让你连平时八成的能力都发挥不出来只有形成肌肉记忆的部分才拿得出来。