美团算法高频题面经:反转链表、两数之和、有效括号、最长子串、合并区间

106 阅读8分钟

前言

美团算法面试很少考察 Hard 难度题目,出题重心集中在 Medium 题型。虽说题目难度不算顶尖,但是面试官十分看重三点:代码零 Bug、边界条件覆盖完整、时间 / 空间复杂度分析清晰。 高频考点高度锁定五大方向:链表、哈希表、栈、滑动窗口、区间合并。

不同大厂算法考察方向存在明显差异:字节面试 Hard 题占比更高,美团更偏向中等题目的落地实现能力,考察候选人写出健壮可运行代码的基本功。

下面 8 道题目覆盖美团算法面试最高频题型,每一道都包含解题思路、可运行 Java 代码、复杂度分析以及面试官最常抛出的追问。

Q1:反转链表(LeetCode 206)

题目描述:给定一条单链表的头节点,反转整条链表,并返回反转后的新头结点。

迭代解法

使用三个指针 prevcurrnext 遍历链表;每次断开当前节点原有指向,将 curr.next 指向 prev,随后三个指针同步向前移动。遍历结束后 prev 即为新链表头节点。 时间复杂度:\(O(n)\),空间复杂度:\(O(1)\)

ListNode reverseList(ListNode head) {
    ListNode prev = null, curr = head;
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

递归解法

递归深入至链表末尾,回溯阶段反转节点指针:head.next.next = head,同时断开原有指针 head.next = null,防止链表循环。 时间复杂度:\(O(n)\),空间复杂度:\(O(n)\)(递归栈开销)

面试高频追问:如何反转链表的指定区间(LeetCode 92)? 先定位反转区间的起始节点,对区间内链表执行反转操作,最后重新接上断开的链表指针;需要额外处理反转起点就是头节点的边界场景。

Q2:两数之和(LeetCode 1)

题目描述:给定整型数组与目标值,在数组中找到两个元素,使其相加等于目标值,返回两个元素对应的数组下标。

暴力解法

双重循环遍历所有两两组合;时间复杂度 \(O(n^2)\),一般不作为最优解。

哈希表解法

一次遍历数组,遍历过程中计算差值 complement = target - nums[i]。 判断哈希表中是否已经存在该差值:存在则直接返回两个下标;不存在就将当前数值和下标存入哈希表。 时间复杂度:\(O(n)\),空间复杂度:\(O(n)\)

int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int comp = target - nums[i];
        if (map.containsKey(comp)) {
            return new int[]{map.get(comp), i};
        }
        map.put(nums[i], i);
    }
    return new int[]{};
}

面试高频追问:如果输入数组本身有序,如何优化? 采用双指针方案:左指针从头、右指针从尾部开始相向移动;两数之和小于目标则左指针右移,大于目标则右指针左移。该方案无需哈希表,空间复杂度 \(O(1)\)。

Q3:有效的括号(LeetCode 20)

题目描述:输入字符串仅包含 (){}[],判断括号是否有效闭合,左括号必须以正确顺序匹配对应类型的右括号。

栈解法

遍历字符串:遇到左括号直接入栈;遇到右括号,检查栈顶元素是否为对应左括号,匹配成功则弹出栈顶元素;栈为空或者括号不匹配直接判定无效。遍历完成栈为空代表全部匹配成功。 时间复杂度:\(O(n)\),空间复杂度:\(O(n)\)

boolean isValid(String s) {
    Stack<Character> stack = new Stack<>();
    Map<Character, Character> map = Map.of(')', '(', '}', '{', ']', '[');
    for (char c : s.toCharArray()) {
        if (map.containsKey(c)) {
            if (stack.isEmpty() || !stack.peek().equals(map.get(c))) {
                return false;
            }
            stack.pop();
        } else {
            stack.push(c);
        }
    }
    return stack.isEmpty();
}

面试高频追问:最少移除多少个括号,可以让字符串变成有效括号? 一次遍历统计未匹配的左括号、右括号数量,二者相加即为最少移除次数。

Q4:无重复字符的最长子串(LeetCode 3)

题目描述:给定字符串,找出其中不含有重复字符的最长子串的长度。

滑动窗口解法

左右指针组成滑动窗口,右指针不断向右扩张窗口范围;当检测到重复字符,左指针移动至重复字符下一位。HashMap 记录字符最新出现的下标位置,全程更新最长子串长度。 时间复杂度:\(O(n)\),空间复杂度:\(O(\min(n,m))\),m 代表字符集大小

int lengthOfLongestSubstring(String s) {
    Map<Character, Integer> map = new HashMap<>();
    int max = 0, left = 0;
    for (int right = 0; right < s.length(); right++) {
        char c = s.charAt(right);
        if (map.containsKey(c)) {
            left = Math.max(left, map.get(c) + 1);
        }
        map.put(c, right);
        max = Math.max(max, right - left + 1);
    }
    return max;
}

面试高频追问:滑动窗口通用解题模板是什么?

  1. 右指针向右扩展窗口,更新窗口内状态;
  2. 判断当前窗口是否满足题目约束;
  3. 满足条件时统计结果;不满足条件则移动左指针收缩窗口。

Q5:合并区间(LeetCode 56)

题目描述:给出若干区间数组 [start, end],合并所有存在重叠的区间,返回合并完成后的区间列表。

解题思路

第一步按照区间起始位置从小到大排序;遍历区间列表,判断当前区间是否与结果列表最后一个区间重叠:重叠则合并区间,更新区间右端最大值;不重叠直接加入结果集合。 时间复杂度:\(O(n \log n)\)(排序开销),空间复杂度:\(O(n)\)

int[][] merge(int[][] intervals) {
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    List<int[]> result = new ArrayList<>();
    for (int[] interval : intervals) {
        if (result.isEmpty() || result.get(result.size()-1)[1] < interval[0]) {
            result.add(interval);
        } else {
            result.get(result.size()-1)[1] = Math.max(result.get(result.size()-1)[1], interval[1]);
        }
    }
    return result.toArray(new int[0][]);
}

面试高频追问:插入新区间如何处理(LeetCode 57)? 方案一:新区间追加至原数组,排序后复用合并区间逻辑; 方案二:一次遍历找到插入位置,原地完成区间合并,无需二次排序。

Q6:LRU 缓存设计(LeetCode 146)

题目描述:设计一个 LRU(最近最少使用)缓存,实现 get(key)put(key,value) 两个接口,两个操作时间复杂度都需要达到 \(O(1)\)。

核心设计方案

采用「HashMap + 双向链表」的数据结构。HashMap 保存 key 到链表节点的映射;双向链表维护数据访问顺序,链表头部存放最近访问过的数据,尾部存放最久未访问数据。

  • get:查到节点后,将节点移动至链表头部;查无结果返回‑1;
  • put:key 存在则更新数值并移到头部;不存在新建节点插入头部;缓存容量已满时删除链表尾部最久未使用节点。

面试高频追问:Java 的 LinkedHashMap 是否可以实现 LRU? 可以实现。构造函数传入 accessOrder=true,并重写 removeEldestEntry 即可快速实现 LRU;但是手写面试题时面试官一般要求自主实现 HashMap + 双向链表版本,不推荐直接使用 LinkedHashMap 偷懒。

Q7:二分查找及其常见变体

基础二分查找:在有序数组内查找目标值,时间复杂度 \(O(\log n)\)。

高频变体题型:

  1. 查找第一个大于等于 target 的下标(lower_bound)
  2. 查找最后一个小于等于 target 的下标
  3. 旋转升序数组查找最小值
  4. 旋转升序数组搜索目标元素

面试高频追问:什么场景适合使用二分查找? 有序数组搜索问题;答案具备单调性特征的题目,例如「最小化最大值」「最大化最小值」类题型。

Q8:美团算法面试备考指南

高频刷题清单

链表(反转链表、合并链表、环形链表)、哈希表(两数之和、字母异位词)、栈(有效括号、最小栈)、滑动窗口(最长无重复子串)、区间(合并、插入区间)、二分查找。

备考建议

  1. 每个题型刷 5‑10 道中等难度题目,整理出对应题型解题模板;
  2. 面试答题顺序:先口头讲解完整解题思路 → 再动手编写代码 → 手动带入测试用例走一遍流程校验;

高频扣分避坑清单

  • 忽略空数组、空链表边界判断;
  • 链表遍历忘记判空,出现空指针异常;
  • 数组下标处理不当引发越界;
  • 两数相加没有考虑整型溢出;
  • 写完代码直接提交,没有自测验证。

小结

美团客户端算法面试重在考察基础算法的工程落地能力。刷题过程不要只追求 AC 通过,要主动思考每道题面试官可能抛出的追问,同时养成边界自测习惯,面试中才能稳稳拿下算法环节。


面试Tips:美团算法面Medium为主,链表题(反转、找环)、哈希表(两数之和)、栈(有效括号)、滑动窗口(最长子串)、区间合并是高频。代码要能跑通、边界要处理、复杂度要讲清。面试时先讲思路确认方向对再写代码,别闷头写完了发现方向错了。

下一篇进入外卖系统专项——实时定位系统、订单状态机、骑手调度算法理解、多端数据同步。


刷过LeetCode Hot 100的同学扣1,你觉得美团算法面试最难的是哪类题?

本系列连载中,关注不迷路,下一篇:美团架构师(外卖系统专项)面试真题

系列简介:Android大厂面经连载,覆盖字节跳动、腾讯、阿里、美团等40+企业,从初级到架构师全岗位覆盖。每篇文章包含真实面试题+详细答案+代码示例,帮你拿到大厂Offer。