大厂算法面试·真题溯源 + 追问实战

四季读书网 2 0
大厂算法面试·真题溯源 + 追问实战
算法目录表:

题号

题目

LeetCode

难度

大厂

考频

1

两数之和

1

字节/阿里/腾讯

⭐⭐⭐⭐⭐

2

第K大

215

⭐⭐

字节/腾讯

⭐⭐⭐⭐

3

盛水容器

11

⭐⭐

字节/阿里

⭐⭐⭐⭐

4

移动零

283

微软/百度

⭐⭐⭐

5

颜色分类

75

⭐⭐

字节/腾讯

⭐⭐⭐

6

只出现一次

136

腾讯/阿里

⭐⭐⭐⭐

7

多数元素

169

字节/美团

⭐⭐⭐⭐

8

合并区间

56

⭐⭐

字节/阿里

⭐⭐⭐⭐

9

反转链表

206

全大厂

⭐⭐⭐⭐⭐

10

反转链表II

92

⭐⭐

字节/腾讯

⭐⭐⭐

11

环形链表I

141

字节/腾讯

⭐⭐⭐⭐⭐

12

环形链表II

142

⭐⭐

阿里/拼多多

⭐⭐⭐⭐

13

合并有序链表

21

阿里/腾讯

⭐⭐⭐⭐⭐

14

两两交换

24

⭐⭐

字节/微软

⭐⭐⭐

15

删除倒数N

19

⭐⭐

字节/阿里

⭐⭐⭐⭐

16

回文链表

234

⭐⭐

腾讯/美团

⭐⭐⭐

17

无重复子串

3

⭐⭐

全大厂

⭐⭐⭐⭐⭐

18

最长回文子串

5

⭐⭐

字节/阿里

⭐⭐⭐⭐

19

有效括号

20

字节/拼多多

⭐⭐⭐⭐

20

字符串相加

415

字节/美团

⭐⭐⭐

21

字符串相乘

43

⭐⭐

阿里/腾讯

⭐⭐⭐

22

翻转单词

151

⭐⭐

字节/微软

⭐⭐⭐

23

最小覆盖子串

76

⭐⭐⭐

字节/阿里

⭐⭐⭐⭐

24

最长公共前缀

14

腾讯/美团

⭐⭐⭐

25

层序遍历

102

⭐⭐

阿里/百度

⭐⭐⭐⭐⭐

26

最大深度

104

全大厂

⭐⭐⭐⭐⭐

27

二叉树直径

543

⭐⭐

字节/腾讯

⭐⭐⭐⭐

28

翻转二叉树

226

谷歌/字节

⭐⭐⭐⭐

29

对称二叉树

101

腾讯/百度

⭐⭐⭐⭐

30

路径总和

112

阿里/拼多多

⭐⭐⭐

31

BST的LCA

235

⭐⭐

阿里/腾讯

⭐⭐⭐⭐

32

验证BST

98

⭐⭐

字节/亚马逊

⭐⭐⭐⭐⭐

33

爬楼梯

70

腾讯/阿里

⭐⭐⭐⭐⭐

34

最大子数组

53

⭐⭐

字节/美团

⭐⭐⭐⭐⭐

35

打家劫舍

198

⭐⭐

字节/阿里

⭐⭐⭐⭐

36

打家劫舍II

213

⭐⭐

字节/腾讯

⭐⭐⭐

37

零钱兑换

322

⭐⭐

亚马逊/微软

⭐⭐⭐⭐

38

最长递增子序列

300

⭐⭐

腾讯/阿里

⭐⭐⭐⭐

39

最长公共子序列

1143

⭐⭐

阿里/腾讯

⭐⭐⭐⭐

40

最小路径和

64

⭐⭐

字节/美团

⭐⭐⭐

41

每日温度

739

⭐⭐

字节/阿里

⭐⭐⭐⭐

42

最大矩形

84

⭐⭐⭐

字节/腾讯

⭐⭐⭐

43

全排列

46

⭐⭐

字节/美团

⭐⭐⭐⭐

44

子集

78

⭐⭐

阿里/腾讯

⭐⭐⭐⭐

45

组合总和

39

⭐⭐

字节/百度

⭐⭐⭐

46

单词搜索

79

⭐⭐

字节/亚马逊

⭐⭐⭐

47

数据流中位数

295

⭐⭐⭐

字节/阿里

⭐⭐⭐⭐

48

前K高频

347

⭐⭐

字节/腾讯

⭐⭐⭐⭐

49

LRU缓存

146

⭐⭐

字节/阿里/腾讯

⭐⭐⭐⭐⭐

50

搜索旋转数组

33

⭐⭐

字节/阿里

⭐⭐⭐⭐⭐

使用建议

  1. 第一阶段:按LeetCode题号刷原题,夯实基础,熟悉每道题的核心解法

  2. 第二阶段:看"方案B"追问链,自己模拟面试回答,锻炼临场应变和思路拓展能力

  3. 第三阶段:找朋友按追问链模拟面试,适应面试节奏,查漏补缺

一、数组 & 哈希表(1-8)

1. 两数之和

【方案A · 真题溯源】

字段

内容

LeetCode

1. Two Sum

难度

⭐ 简单

大厂

字节、阿里、腾讯、美团、百度

考频

⭐⭐⭐⭐⭐(100%必考)

真实面经

字节一面、阿里二面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下两数之和。

def twoSum(nums, target):    d = {}    for i, n in enumerate(nums):        if target - n in d:            return [d[target-n], i]        d[n] = i

Q2(扩展):三数之和呢?

> 排序 + 双指针,O(n²)。固定第一个数,然后双指针找另外两个。

Q3(扩展):如果数组有100亿个数,内存放不下,怎么办?

> 外部排序 + 分块,或者哈希分桶,同一个桶内计算。

Q4(分布式):分布在100台机器上,每台1亿个数?

> MapReduce思想:哈希分桶,同一桶内计算。或者用分布式哈希表。

Q5(变种):如果要求返回所有不重复的三元组?

> 排序 + 跳过重复数字,固定第一个后双指针,同时跳过重复。


2. 数组中的第K个最大元素

【方案A · 真题溯源】

字段

内容

LeetCode

215. Kth Largest Element

难度

⭐⭐ 中等

大厂

字节、腾讯、亚马逊、微软

考频

⭐⭐⭐⭐

真实面经

字节二面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下。

import heapqdef findKthLargest(nums, k):    return heapq.nlargest(k, nums)[-1]

Q2(手写):不用堆,手写快排分区方法。

> 快速选择(Quick Select),平均O(n),最坏O(n²)。每次选pivot分区,只递归一边。

Q3(优化):如何避免最坏情况?

> 随机选择pivot / 三数取中法 / 中位数的中位数(BFPRT)。

Q4(海量数据):如果数据量100亿,内存放不下?

> 使用最小堆,维护K个最大元素,一次遍历,O(n log K)。


3. 盛最多水的容器

【方案A · 真题溯源】

字段

内容

LeetCode

11. Container With Most Water

难度

⭐⭐ 中等

大厂

字节、阿里、腾讯

考频

⭐⭐⭐⭐

真实面经

字节一面、阿里一面

【方案B · 面试追问链】

Q1(基础):写一下。

def maxArea(height):    l, r = 0len(height)-1    res = 0    while l < r:        res = max(res, min(height[l], height[r]) * (r-l))        if height[l] < height[r]: l += 1        else: r -= 1    return res

Q2(原理):为什么移动短边?

> 移动短边可能找到更高的边,增加面积;移动长边只会让宽度减小,高度不会超过短边,面积只会变小。

Q3(变种):如果有负数高度?

> 容器高度不能为负,可以先过滤或直接返回0。

Q4(二维):如果是二维矩阵,找最大矩形水池?

> 按行枚举,每行看作一个直方图,用单调栈求最大矩形。


4. 移动零

【方案A · 真题溯源】

字段

内容

LeetCode

283. Move Zeroes

难度

⭐ 简单

大厂

微软、亚马逊、百度

考频

⭐⭐⭐

真实面经

百度一面、微软一面

【方案B · 面试追问链】

Q1(基础):写一下。

def moveZeroes(nums):    idx = 0    for n in nums:        if n != 0:            nums[idx] = n            idx += 1    nums[idx:] = [0] * (len(nums)-idx)

Q2(要求):能否保持非零元素的相对顺序?

> 当前方法保持了相对顺序。

Q3(要求):能否O(1)空间?

> 当前方法O(1)空间。

Q4(要求):能否一次遍历且不额外用数组?

> 双指针交换法:遇到非零就与左指针交换,左指针++。


5. 颜色分类(荷兰国旗)

【方案A · 真题溯源】

字段

内容

LeetCode

75. Sort Colors

难度

⭐⭐ 中等

大厂

字节、腾讯、拼多多

考频

⭐⭐⭐

真实面经

字节二面、拼多多一面

【方案B · 面试追问链】

Q1(基础):写一下。

def sortColors(nums): l, mid, r = 00, len(nums)-1    while mid <= r:        if nums[mid] == 0:            nums[l], nums[mid] = nums[mid], nums[l]            l += 1; mid += 1        elif nums[mid] == 2:            nums[mid], nums[r] = nums[r], nums[mid]            r -= 1        else:            mid += 1

Q2(原理):为什么遇到0要移动l和mid,遇到2只移动r?

> 0换过来的一定是0或1(已扫描过),所以mid可以前进;2换过来的未扫描过,需要重新检查。

Q3(变种):如果是4种颜色(0,1,2,3)?

> 双指针法不再适用,用计数排序(O(n)时间,O(k)空间)或桶排序。

Q4(要求):要求O(n)时间,O(1)空间。

> 当前方法满足。


6. 只出现一次的数字

【方案A · 真题溯源】

字段

内容

LeetCode

136. Single Number

难度

⭐ 简单

大厂

阿里、腾讯、小米

考频

⭐⭐⭐⭐

真实面经

腾讯一面、阿里一面

【方案B · 面试追问链】

Q1(基础):写一下。

def singleNumber(nums):    res = 0    for n in nums:        res ^= n    return res

Q2(原理):为什么异或能行?

> a^a=0,0^a=a,异或满足交换律结合律。

Q3(变种):如果其他数字都出现3次,只有1个出现1次?

> 用位运算:统计每一位上1的个数 mod 3。

Q4(变种):如果有两个数字只出现1次,其他出现2次?

> 异或得到x^y,找到某一位为1,分组异或。


7. 多数元素

【方案A · 真题溯源】

字段

内容

LeetCode

169. Majority Element

难度

⭐ 简单

大厂

字节、美团、百度

考频

⭐⭐⭐⭐

真实面经

美团一面、字节一面

【方案B · 面试追问链】

Q1(基础):写一下。

def majorityElement(nums):    cnt, res = 00    for n in nums:        if cnt == 0:            res = n        cnt += 1 if n == res else -1    return res

Q2(原理):摩尔投票法为什么正确?

> 多数元素出现的次数比其他所有元素出现次数之和还要多,正负抵消后剩下的就是多数元素。

Q3(其他解法):还有其他解法吗?

> 哈希表O(n)空间;排序后取中间O(n log n);分治法。

Q4(变种):找出现次数超过 n/3 的元素?

> 最多有两个,用两个候选人的摩尔投票。


8. 合并区间

【方案A · 真题溯源】

字段

内容

LeetCode

56. Merge Intervals

难度

⭐⭐ 中等

大厂

字节、阿里、滴滴

考频

⭐⭐⭐⭐

真实面经

字节一面、滴滴一面

【方案B · 面试追问链】

Q1(基础):写一下。

def merge(intervals):    intervals.sort()    res = [intervals[0]]    for s, e in intervals[1:]:        if s <= res[-1][1]:            res[-1][1] = max(res[-1][1], e)        else:            res.append([s, e])    return res

Q2(变种):插入一个新区间并合并。

> 二分找到插入位置,然后类似合并。

Q3(变种):找两个区间列表的交集。

> 双指针,每次取重叠部分。

Q4(大数据):区间长度总和10^9,区间个数10^6?

> 排序 + 扫描,O(n log n) 可以接受。可以进一步用扫描线。


二、链表(9-16)

9. 反转链表

【方案A · 真题溯源】

字段

内容

LeetCode

206. Reverse Linked List

难度

⭐ 简单

大厂

所有大厂必考

考频

⭐⭐⭐⭐⭐

真实面经

字节/阿里/腾讯/美团 一面

【方案B · 面试追问链】

Q1(基础):写一下(迭代)。

def reverseList(head):    pre, cur = None, head    while cur:        nxt = cur.next        cur.next = pre        pre, cur = cur, nxt    return pre

Q2(要求):递归写法。

def reverseList(head):    if not head or not head.next:        return head    new_head = reverseList(head.next)    head.next.next = head    head.next = None    return new_head

Q3(扩展):反转链表 II(区间反转)。

> 找到pre节点,局部反转。

Q4(扩展):K个一组反转链表。

> 先判断是否有K个节点,然后反转K个,递归或迭代。


10. 反转链表 II(区间反转)

【方案A · 真题溯源】

字段

内容

LeetCode

92. Reverse Linked List II

难度

⭐⭐ 中等

大厂

字节、腾讯、美团

考频

⭐⭐⭐

真实面经

字节二面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下。

def reverseBetween(head, left, right):    dummy = ListNode(next=head)    pre = dummy    for _ in range(left-1):        pre = pre.next    cur = pre.next    for _ in range(right-left):        nxt = cur.next        cur.next = nxt.next        nxt.next = pre.next        pre.next = nxt    return dummy.next

Q2(原理):为什么用头插法?

> 头插法可以原地反转,不需要额外空间。

Q3(边界):如果left=1?

> dummy节点处理,无需特殊判断。

Q4(要求):如果用递归实现?

> 比较复杂,一般用迭代。


11. 环形链表 I

【方案A · 真题溯源】

字段

内容

LeetCode

141. Linked List Cycle

难度

⭐ 简单

大厂

字节、腾讯、微软

考频

⭐⭐⭐⭐⭐

真实面经

字节一面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下。

def hasCycle(head):    slow = fast = head    while fast and fast.next:        slow = slow.next        fast = fast.next.next        if slow == fast:            return True    return False

Q2(原理):为什么快慢指针一定会相遇?

> 快指针比慢指针快1步,相当于追及问题,环内一定能追上。

Q3(空间):空间复杂度?

> O(1)。

Q4(变种):如何找到入环点?

> 相遇后,一个从头开始,一个从相遇点,同速走,再次相遇点即入环点。


12. 环形链表 II(找入环点)

【方案A · 真题溯源】

字段

内容

LeetCode

142. Linked List Cycle II

难度

⭐⭐ 中等

大厂

阿里、拼多多、亚马逊

考频

⭐⭐⭐⭐

真实面经

阿里二面、拼多多一面

【方案B · 面试追问链】

Q1(基础):写一下。

def detectCycle(head):    slow = fast = head    while fast and fast.next:        slow = slow.next        fast = fast.next.next        if slow == fast:            p = head            while p != slow:                p = p.next                slow = slow.next            return p    return None

Q2(推导):为什么从头和相遇点同速走就能找到入环点?

> 设入环前长度a,环长度b,相遇时slow走a+b,fast走a+b+kb,fast是slow两倍,可得a = (k-1)b + (b - x),所以从头走a步和从相遇点走b-x步会重合。

Q3(要求):空间复杂度?

> O(1)。

Q4(变种):如果要求返回环的长度?

> 相遇后继续走,再次相遇时走的步数即环长。


13. 合并两个有序链表

【方案A · 真题溯源】

字段

内容

LeetCode

21. Merge Two Sorted Lists

难度

⭐ 简单

大厂

阿里、腾讯、快手

考频

⭐⭐⭐⭐⭐

真实面经

阿里一面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下。

def mergeTwoLists(l1, l2):    dummy = cur = ListNode()    while l1 and l2:        if l1.val < l2.val:            cur.next = l1            l1 = l1.next        else:            cur.next = l2            l2 = l2.next        cur = cur.next    cur.next = l1 or l2    return dummy.next

Q2(要求):递归写法。

def mergeTwoLists(l1, l2):    if not l1: return l2    if not l2: return l1    if l1.val < l2.val:        l1.next = mergeTwoLists(l1.next, l2)        return l1    else:        l2.next = mergeTwoLists(l1, l2.next)        return l2

Q3(扩展):合并K个有序链表。

> 分治法(两两合并)或使用最小堆。

Q4(复杂度):时间复杂度?

> O(m+n)。


14. 两两交换链表节点

【方案A · 真题溯源】

字段

内容

LeetCode

24. Swap Nodes in Pairs

难度

⭐⭐ 中等

大厂

字节、微软

考频

⭐⭐⭐

真实面经

字节一面、微软一面

【方案B · 面试追问链】

Q1(基础):写一下。

def swapPairs(head):    dummy = ListNode(next=head)    pre = dummy    while pre.next and pre.next.next:        a = pre.next        b = a.next        pre.next, b.next, a.next = b, a, b.next        pre = a    return dummy.next

Q2(要求):递归写法。

def swapPairs(head):    if not head or not head.next:        return head    new_head = head.next    head.next = swapPairs(new_head.next)    new_head.next = head    return new_head

Q3(变种):K个一组反转链表。

> 更复杂,需要判断是否有K个节点。

Q4(要求):空间复杂度?

> 迭代O(1),递归O(n)栈空间。


15. 删除链表倒数第N个节点

【方案A · 真题溯源】

字段

内容

LeetCode

19. Remove Nth Node From End

难度

⭐⭐ 中等

大厂

字节、阿里、百度

考频

⭐⭐⭐⭐

真实面经

字节一面、阿里一面

【方案B · 面试追问链】

Q1(基础):写一下。

def removeNthFromEnd(head, n):    dummy = ListNode(next=head)    fast = slow = dummy    for _ in range(n+1):        fast = fast.next    while fast:        slow = slow.next        fast = fast.next    slow.next = slow.next.next    return dummy.next

Q2(原理):为什么快指针先走n+1步?

> 让slow指向要删除节点的前一个节点。

Q3(边界):如果删除的是头节点?

> dummy节点处理,返回dummy.next即可。

Q4(要求):如果只能遍历一次?

> 当前方法就是一次遍历。


16. 回文链表

【方案A · 真题溯源】

字段

内容

LeetCode

234. Palindrome Linked List

难度

⭐⭐ 中等

大厂

腾讯、美团、小米

考频

⭐⭐⭐

真实面经

腾讯一面、美团一面

【方案B · 面试追问链】

Q1(基础):写一下。

def isPalindrome(head):    slow = fast = head    pre = None    while fast and fast.next:        fast = fast.next.next        nxt = slow.next        slow.next = pre        pre = slow        slow = nxt    if fast:        slow = slow.next    while pre and pre.val == slow.val:        pre = pre.next        slow = slow.next    return not pre

Q2(原理):这个代码做了什么事?

> 快慢指针找中点,前半部分反转,然后比较。

Q3(空间):空间复杂度?

> O(1)。

Q4(要求):如果用数组辅助?

> 更简单但O(n)空间,面试可能不满意。


三、字符串(17-24)

17. 无重复字符的最长子串

【方案A · 真题溯源】

字段

内容

LeetCode

3. Longest Substring Without Repeating

难度

⭐⭐ 中等

大厂

所有大厂Top1高频

考频

⭐⭐⭐⭐⭐

真实面经

【方案B · 面试追问链】

Q1(基础):写一下。

def lengthOfLongestSubstring(s):    d = {}    left = res = 0    for right, c in enumerate(s):        if c in d and d[c] >= left:            left = d[c] + 1        d[c] = right        res = max(res, right-left+1)    return res

Q2(原理):为什么左指针要跳到重复字符的下一位?

> 保证窗口内无重复。

Q3(变种):如果允许最多K个重复字符?

> 滑动窗口 + 计数,当超过K时移动左指针。

Q4(变种):如果字符串长度10^6?

> O(n)可以过。


18. 最长回文子串

【方案A · 真题溯源】

字段

内容

LeetCode

5. Longest Palindromic Substring

难度

⭐⭐ 中等

大厂

字节、阿里、腾讯

考频

⭐⭐⭐⭐

真实面经

字节二面、阿里二面

【方案B · 面试追问链】

Q1(基础):写一下(中心扩展)。

def longestPalindrome(s):    def expand(l, r):        while l >= 0 and r < len(s) and s[l] == s[r]:            l -= 1            r += 1        return s[l+1:r]    res = ''    for i in range(len(s)):        res = max(res, expand(i,i), expand(i,i+1), key=len)    return res

Q2(复杂度):时间复杂度?

> O(n²),空间O(1)。

Q3(优化):能否O(n)?

> Manacher算法,但面试一般不要求写。

Q4(变种):最长回文子序列。

> DP,不是连续子串。


19. 有效的括号

【方案A · 真题溯源】

字段

内容

LeetCode

20. Valid Parentheses

难度

⭐ 简单

大厂

字节、拼多多、百度

考频

⭐⭐⭐⭐

真实面经

字节一面、拼多多一面

【方案B · 面试追问链】

Q1(基础):写一下。

def isValid(s):    stack = []    d = {')':'('']':'[''}':'{'}    for c in s:        if c not in d:            stack.append(c)        else:            if not stack or stack.pop() != d[c]:                return False    return not stack

Q2(变种):如果括号有优先级(如{ [ ( ) ] } 嵌套必须按顺序)?

> 需要额外检查栈顶优先级。

Q3(变种):如果字符串很长(10^7)?

> O(n)可以过,栈最大深度n。

Q4(变种):返回最长有效括号子串长度?

> 用栈存下标,LeetCode 32。


20. 字符串相加

【方案A · 真题溯源】

字段

内容

LeetCode

415. Add Strings

难度

⭐ 简单

大厂

字节、美团、快手

考频

⭐⭐⭐

真实面经

字节一面、快手一面

【方案B · 面试追问链】

Q1(基础):写一下。

def addStrings(num1, num2):    i, j, carry = len(num1)-1len(num2)-10    res = []    while i >= 0 or j >= 0 or carry:        a = int(num1[i]) if i >= 0 else 0        b = int(num2[j]) if j >= 0 else 0        carry, t = divmod(a + b + carry, 10)        res.append(str(t))        i -= 1; j -= 1    return ''.join(res[::-1])

Q2(变种):字符串相乘。

> LeetCode 43,用数组存每位结果。

Q3(变种):二进制相加。

> 类似,进位改为2。

Q4(大数):如果数字长度10^6?

> 可以,O(n)。


21. 字符串相乘

【方案A · 真题溯源】

字段

内容

LeetCode

43. Multiply Strings

难度

⭐⭐ 中等

大厂

阿里、腾讯、华为

考频

⭐⭐⭐

真实面经

阿里二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

def multiply(num1, num2):    if num1 == "0" or num2 == "0"return "0"    m, n = len(num1), len(num2)    res = [0] * (m + n)    for i in range(m-1, -1, -1):        a = int(num1[i])        for j in range(n-1, -1, -1):            b = int(num2[j])            res[i+j+1] += a * b            res[i+j] += res[i+j+1] // 10            res[i+j+1] %= 10    idx = 0    while res[idx] == 0: idx += 1    return ''.join(map(str, res[idx:]))

Q2(原理):为什么数组长度是m+n?

> 两数相乘,结果最多m+n位。

Q3(优化):时间复杂度?

> O(m*n)。

Q4(变种):如果用快速傅里叶变换(FFT)?

> O(n log n),但面试不要求。


22. 翻转字符串里的单词

【方案A · 真题溯源】

字段

内容

LeetCode

151. Reverse Words in a String

难度

⭐⭐ 中等

大厂

字节、微软、百度

考频

⭐⭐⭐

真实面经

字节一面、微软一面

【方案B · 面试追问链】

Q1(基础):写一下。

def reverseWords(s):    return ' '.join(s.split()[::-1])

Q2(要求):不使用split。

> 遍历字符串,提取单词到列表,然后逆序拼接。

Q3(要求):O(1)额外空间。

> 先反转整个字符串,再反转每个单词,同时处理多余空格。

Q4(变种):反转字符串里的单词顺序,但保持单词内字符顺序不变。

> 就是本题。


23. 最小覆盖子串

【方案A · 真题溯源】

字段

内容

LeetCode

76. Minimum Window Substring

难度

⭐⭐⭐ 困难

大厂

字节、阿里、腾讯

考频

⭐⭐⭐⭐

真实面经

字节三面、阿里二面

【方案B · 面试追问链】

Q1(基础):写一下。

def minWindow(s, t):    from collections import Counter    need = Counter(t)    window = {}    left = valid = 0    start, min_len = 0float('inf')    for right, c in enumerate(s):        if c in need:            window[c] = window.get(c, 0) + 1            if window[c] == need[c]:                valid += 1        while valid == len(need):            if right - left + 1 < min_len:                start, min_len = left, right - left + 1            d = s[left]            if d in need:                if window[d] == need[d]:                    valid -= 1                window[d] -= 1            left += 1    return s[start:start+min_len] if min_len != float('inf'else ''

Q2(复杂度):时间复杂度?

> O(m+n),每个字符最多进窗口一次、出窗口一次。

Q3(变种):如果t有重复字符?

> 当前解法已处理,need计数匹配。

Q4(变种):找所有异位词的子串?

> LeetCode 438,类似滑动窗口。


24. 最长公共前缀

【方案A · 真题溯源】

字段

内容

LeetCode

14. Longest Common Prefix

难度

⭐ 简单

大厂

腾讯、美团、小米

考频

⭐⭐⭐

真实面经

腾讯一面、美团一面

【方案B · 面试追问链】

Q1(基础):写一下。

def longestCommonPrefix(strs):    if not strs: return ''    pre = strs[0]    for s in strs[1:]:        while not s.startswith(pre):            pre = pre[:-1]            if not pre: return ''    return pre

Q2(复杂度):时间复杂度?

> O(S),S为所有字符串长度和。

Q3(优化):如何优化?

> 横向扫描、纵向扫描、分治法、二分查找。

Q4(变种):如果字符串数组很大?

> 纵向扫描可以提前终止。


四、二叉树 & BFS/DFS(25-32)

25. 二叉树的层序遍历

【方案A · 真题溯源】

字段

内容

LeetCode

102. Binary Tree Level Order

难度

⭐⭐ 中等

大厂

阿里、百度、京东

考频

⭐⭐⭐⭐⭐

真实面经

阿里一面、百度一面

【方案B · 面试追问链】

Q1(基础):写一下。

def levelOrder(root):    if not root: return []    q = deque([root])    res = []    while q:        level = []        for _ in range(len(q)):            node = q.popleft()            level.append(node.val)            if node.left: q.append(node.left)            if node.right: q.append(node.right)        res.append(level)    return res

Q2(变种):锯齿形层序遍历。

> 偶数层反转或用双端队列。

Q3(变种):二叉树的右视图。

> 每层最后一个节点。

Q4(变种):二叉树的最大宽度。

> BFS时记录节点编号。


26. 二叉树的最大深度

【方案A · 真题溯源】

字段

内容

LeetCode

104. Maximum Depth of Binary Tree

难度

⭐ 简单

大厂

所有大厂基础题

考频

⭐⭐⭐⭐⭐

真实面经

字节/阿里/腾讯 一面

【方案B · 面试追问链】

Q1(基础):写一下。

def maxDepth(root):    if not root: return 0    return max(maxDepth(root.left), maxDepth(root.right)) + 1

Q2(要求):迭代写法。

> BFS层序遍历,每层深度+1。

Q3(变种):最小深度。

> 叶子节点时返回深度,注意单支情况。

Q4(变种):N叉树的最大深度。

> 遍历所有子节点取max。


27. 二叉树的直径

【方案A · 真题溯源】

字段

内容

LeetCode

543. Diameter of Binary Tree

难度

⭐⭐ 中等

大厂

字节、腾讯、美团

考频

⭐⭐⭐⭐

真实面经

字节二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

def diameterOfBinaryTree(root):    res = 0    def dfs(node):        nonlocal res        if not node: return 0        l = dfs(node.left)        r = dfs(node.right)        res = max(res, l + r)        return max(l, r) + 1    dfs(root)    return res

Q2(原理):为什么是左高度+右高度?

> 直径是经过该节点的最长路径 = 左子树深度 + 右子树深度。

Q3(变种):如果求的是节点数而不是边数?

> 返回l+r+1。

Q4(变种):二叉树的最大路径和。

> LeetCode 124,DP类似。


28. 翻转二叉树

【方案A · 真题溯源】

字段

内容

LeetCode

226. Invert Binary Tree

难度

⭐ 简单

大厂

谷歌、字节、阿里

考频

⭐⭐⭐⭐

真实面经

字节一面、谷歌一面

【方案B · 面试追问链】

Q1(基础):写一下。

def invertTree(root):    if not root: return None    root.left, root.right = invertTree(root.right), invertTree(root.left)    return root

Q2(要求):迭代写法。

> BFS或DFS栈。

Q3(变种):对称二叉树。

> 递归判断左右镜像。

Q4(幽默):知道这个题为什么火吗?

> Homebrew作者面试Google没写出来。


29. 对称二叉树

【方案A · 真题溯源】

字段

内容

LeetCode

101. Symmetric Tree

难度

⭐ 简单

大厂

腾讯、百度、小米

考频

⭐⭐⭐⭐

真实面经

腾讯一面、百度一面

【方案B · 面试追问链】

Q1(基础):写一下。

def isSymmetric(root):    def dfs(l, r):        if not l and not r: return True        if not l or not r: return False        return l.val == r.val and dfs(l.left, r.right) and dfs(l.right, r.left)    return dfs(root, root)

Q2(要求):迭代写法。

> 队列成对入队出队。

Q3(变种):相同的树。

> LeetCode 100,更简单。

Q4(变种):另一棵树的子树。

> LeetCode 572。


30. 路径总和

【方案A · 真题溯源】

字段

内容

LeetCode

112. Path Sum

难度

⭐ 简单

大厂

阿里、拼多多、网易

考频

⭐⭐⭐

真实面经

阿里一面、拼多多一面

【方案B · 面试追问链】

Q1(基础):写一下。

def hasPathSum(root, targetSum):    if not root: return False    if not root.left and not root.right:        return root.val == targetSum    return hasPathSum(root.left, targetSum-root.val) or hasPathSum(root.right, targetSum-root.val)

Q2(变种):返回所有路径。

> LeetCode 113,带path回溯。

Q3(变种):路径总和 III(任意起点终点)。

> LeetCode 437,前缀和+哈希。

Q4(变种):二叉树中的最大路径和。

> LeetCode 124,困难题。


31. 二叉搜索树的最近公共祖先

【方案A · 真题溯源】

字段

内容

LeetCode

235. Lowest Common Ancestor of BST

难度

⭐⭐ 中等

大厂

阿里、腾讯、字节

考频

⭐⭐⭐⭐

真实面经

阿里一面、腾讯一面

【方案B · 面试追问链】

Q1(基础):写一下。

def lowestCommonAncestor(root, p, q):    if root.val > p.val and root.val > q.val:        return lowestCommonAncestor(root.left, p, q)    if root.val < p.val and root.val < q.val:        return lowestCommonAncestor(root.right, p, q)    return root

Q2(变种):普通二叉树的LCA。

> 递归:左子树有无p/q,右子树有无p/q。

Q3(变种):如果节点不一定存在?

> 先遍历检查是否存在,或返回标志位。

Q4(变种):LCA with parent指针。

> 用哈希表或双指针。


32. 验证二叉搜索树

【方案A · 真题溯源】

字段

内容

LeetCode

98. Validate Binary Search Tree

难度

⭐⭐ 中等

大厂

字节、亚马逊、微软

考频

⭐⭐⭐⭐⭐

真实面经

字节二面、亚马逊一面

【方案B · 面试追问链】

Q1(基础):写一下。

def isValidBST(root):    def dfs(node, l, r):        if not node: return True        if not (l < node.val < r): return False        return dfs(node.left, l, node.val) and dfs(node.right, node.val, r)    return dfs(root, float('-inf'), float('inf'))

Q2(变种):中序遍历法。

> 中序遍历,检查是否严格递增。

Q3(变种):BST找第K小的元素。

> 中序遍历到第K个。

Q4(变种):BST找前驱和后继。

> 左子树最右 / 右子树最左。


五、动态规划 DP(33-40)

33. 爬楼梯

【方案A · 真题溯源】

字段

内容

LeetCode

70. Climbing Stairs

难度

⭐ 简单

大厂

腾讯、阿里、小米

考频

⭐⭐⭐⭐⭐

真实面经

腾讯一面、阿里一面

【方案B · 面试追问链】

Q1(基础):写一下。

def climbStairs(n):    a, b = 11    for _ in range(n-1):        a, b = b, a+b    return b

Q2(变种):最小花费爬楼梯。

> dp[i] = min(dp[i-1], dp[i-2]) + cost[i]。

Q3(变种):一次可以爬1、2、3步。

> dp[i] = dp[i-1]+dp[i-2]+dp[i-3]。

Q4(变种):有障碍物。

> dp[i] = 0 if 有障碍。


34. 最大子数组和

【方案A · 真题溯源】

字段

内容

LeetCode

53. Maximum Subarray

难度

⭐⭐ 中等

大厂

字节、美团、华为

【方案B · 面试追问链】

Q1(基础):写一下。

def maxSubArray(nums):    res = cur = nums[0]    for n in nums[1:]:        cur = max(n, cur+n)        res = max(res, cur)    return res

Q2(变种):返回子数组的起止位置。

记录cur重置时的索引。

Q3(变种):环形数组的最大子数组和。

两种情况:不成环 / 成环(总和-最小子数组)。

Q4(变种):二维矩阵的最大子矩阵和。

枚举行边界,降维成一维。


35. 打家劫舍

【方案A · 真题溯源】

字段

内容

LeetCode

198. House Robber

难度

⭐⭐ 中等

大厂

字节、阿里、拼多多

考频

⭐⭐⭐⭐

真实面经

字节一面、阿里一面

【方案B · 面试追问链】

Q1(基础):写一下。

def rob(nums):    a, b = 00    for n in nums:        a, b = b, max(b, a+n)    return b

Q2(变种):打家劫舍 II(环形)。

拆成两个:不抢首、不抢尾,取max。

Q3(变种):打家劫舍 III(二叉树)。

树形DP,返回[抢当前, 不抢当前]。

Q4(变种):如果房屋是环形且相邻不能抢,但第一个和最后一个也不能同时抢。

就是打家劫舍II。


36. 打家劫舍 II(环形)

【方案A · 真题溯源】

字段

内容

LeetCode

213. House Robber II

难度

⭐⭐ 中等

大厂

字节、腾讯

考频

⭐⭐⭐

真实面经

字节二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

def rob(nums):    def helper(l, r):        a = b = 0        for i in range(l, r):            a, b = b, max(b, a+nums[i])        return b    n = len(nums)    if n == 1return nums[0]    return max(helper(0, n-1), helper(1, n))

Q2(原理):为什么要拆成两个?

环形意味着首尾不能同时选,所以分别排除首和尾。

Q3(变种):如果房屋排列成二叉树?

树形DP。

Q4(变种):如果抢了第一个不能抢最后一个,但抢了最后一个可以抢第一个?

不对称条件,需要特殊处理。


37. 零钱兑换

【方案A · 真题溯源】

字段

内容

LeetCode

322. Coin Change

难度

⭐⭐ 中等

大厂

亚马逊、微软、字节

考频

⭐⭐⭐⭐

真实面经

亚马逊一面、字节二面

【方案B · 面试追问链】

Q1(基础):写一下。

def coinChange(coins, amount):    dp = [float('inf')] * (amount+1)    dp[0] = 0    for c in coins:        for i in range(c, amount+1):            dp[i] = min(dp[i], dp[i-c] + 1)    return dp[amount] if dp[amount] != float('inf'else -1

Q2(变种):求组合数(凑成amount的方法数)。

dp[i] += dp[i-c],LeetCode 518。

Q3(变种):如果硬币数量有限?

多重背包问题。

Q4(变种):如果amount很大(10^6),coins很少?

BFS比DP更优。


38. 最长递增子序列

【方案A · 真题溯源】

字段

内容

LeetCode

300. Longest Increasing Subsequence

难度

⭐⭐ 中等

大厂

腾讯、阿里、字节

考频

⭐⭐⭐⭐

真实面经

腾讯二面、阿里二面

【方案B · 面试追问链】

Q1(基础):写一下(贪心+二分)。

def lengthOfLIS(nums):    tails = []    for n in nums:        if not tails or n > tails[-1]:            tails.append(n)        else:            import bisect            tails[bisect.bisect_left(tails, n)] = n    return len(tails)

Q2(原理):tails数组的含义?

tails[i] 表示长度为 i+1 的递增子序列的最小结尾元素。

Q3(变种):最长连续递增子序列。

更简单,一次遍历。

Q4(变种):最长递增子序列的个数。

需要同时记录个数。


39. 最长公共子序列

【方案A · 真题溯源】

字段

内容

LeetCode

1143. Longest Common Subsequence

难度

⭐⭐ 中等

大厂

阿里、腾讯、华为

考频

⭐⭐⭐⭐

真实面经

阿里二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

def longestCommonSubsequence(text1, text2):    m, n = len(text1), len(text2)    dp = [[0]*(n+1) for _ in range(m+1)]    for i in range(1, m+1):        for j in range(1, n+1):            if text1[i-1] == text2[j-1]:                dp[i][j] = dp[i-1][j-1] + 1            else:                dp[i][j] = max(dp[i-1][j], dp[i][j-1])    return dp[m][n]

Q2(变种):最长公共子串。

要求连续,不同递推式,且需要记录最大值。

Q3(优化):空间优化。

用两行滚动数组。

Q4(变种):最短公共超序列。

返回最短字符串包含两者。


40. 最小路径和

【方案A · 真题溯源】

字段

内容

LeetCode

64. Minimum Path Sum

难度

⭐⭐ 中等

大厂

字节、美团、百度

考频

⭐⭐⭐

真实面经

字节一面、美团一面

【方案B · 面试追问链】

Q1(基础):写一下。

def minPathSum(grid):    m, n = len(grid), len(grid[0])    for i in range(m):        for j in range(n):            if i == 0 and j == 0continue            elif i == 0: grid[i][j] += grid[i][j-1]            elif j == 0: grid[i][j] += grid[i-1][j]            else: grid[i][j] += min(grid[i-1][j], grid[i][j-1])    return grid[-1][-1]

Q2(变种):带障碍物的最小路径和。

有障碍物则路径数=0,LeetCode 63。

Q3(变种):输出路径。

从终点反向回溯。

Q4(变种):空间优化为O(n)。

用一维数组滚动。


六、栈、堆、回溯、设计(41-50)

41. 每日温度

【方案A · 真题溯源】

字段

内容

LeetCode

739. Daily Temperatures

难度

⭐⭐ 中等

大厂

字节、阿里、亚马逊

考频

⭐⭐⭐⭐

真实面经

字节二面、阿里二面

【方案B · 面试追问链】

Q1(基础):写一下。

def dailyTemperatures(T):    res, stack = [0]*len(T), []    for i, t in enumerate(T):        while stack and T[stack[-1]] < t:            idx = stack.pop()            res[idx] = i - idx        stack.append(i)    return res

Q2(变种):下一个更大元素 I。

LeetCode 496,更简单。

Q3(变种):下一个更大元素 II(环形)。

遍历两遍数组。

Q4(变种):接雨水。

单调栈或双指针。


42. 柱状图中最大矩形

【方案A · 真题溯源】

字段

内容

LeetCode

84. Largest Rectangle in Histogram

难度

⭐⭐⭐ 困难

大厂

字节、腾讯、华为

考频

⭐⭐⭐

真实面经

字节三面、腾讯三面

【方案B · 面试追问链】

Q1(基础):写一下。

def largestRectangleArea(heights):    stack, heights = [-1], heights + [0]    res = 0    for i, h in enumerate(heights):        while heights[stack[-1]] > h:            height = heights[stack.pop()]            width = i - stack[-1] - 1            res = max(res, height * width)        stack.append(i)    return res

Q2(原理):为什么最后加0?

保证最后所有元素出栈。

Q3(变种):最大矩形(二维矩阵)。

每一行当作柱状图,调用本题。

Q4(变种):直方图的水量(接雨水)。

不同逻辑。


43. 全排列

【方案A · 真题溯源】

字段

内容

LeetCode

46. Permutations

难度

⭐⭐ 中等

大厂

字节、美团、网易

考频

⭐⭐⭐⭐

真实面经

字节二面、美团二面

【方案B · 面试追问链】

Q1(基础):写一下。

def permute(nums):    res = []    def back(path, rest):        if not rest:            res.append(path)            return        for i in range(len(rest)):            back(path+[rest[i]], rest[:i]+rest[i+1:])    back([], nums)    return res

Q2(变种):全排列 II(有重复数字)。

排序 + visited数组去重。

Q3(变种):下一个排列。

找升序对,交换,反转。

Q4(变种):排列序列(第K个排列)。

数学方法,LeetCode 60。


44. 子集

【方案A · 真题溯源】

字段

内容

LeetCode

78. Subsets

难度

⭐⭐ 中等

大厂

阿里、腾讯、拼多多

考频

⭐⭐⭐⭐

真实面经

阿里二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

def subsets(nums):    res = []    def dfs(path, start):        res.append(path)        for i in range(start, len(nums)):            dfs(path+[nums[i]], i+1)    dfs([], 0)    return res

Q2(变种):子集 II(有重复数字)。

排序 + 跳过重复。

Q3(变种):组合。

固定长度,LeetCode 77。

Q4(变种):二进制枚举。

非回溯,用位运算。


45. 组合总和

【方案A · 真题溯源】

字段

内容

LeetCode

39. Combination Sum

难度

⭐⭐ 中等

大厂

字节、百度、微软

考频

⭐⭐⭐

真实面经

字节二面、百度二面

【方案B · 面试追问链】

Q1(基础):写一下。

def combinationSum(candidates, target):    res = []    def dfs(path, start, remain):        if remain == 0:            res.append(path)            return        for i in range(start, len(candidates)):            if candidates[i] > remain: continue            dfs(path+[candidates[i]], i, remain-candidates[i])    dfs([], 0, target)    return res

Q2(变种):组合总和 II(每个数只能用一次)。

排序 + 跳过重复。

Q3(变种):组合总和 III(固定个数)。

LeetCode 216。

Q4(变种):电话号码的字母组合。

LeetCode 17。


46. 单词搜索

【方案A · 真题溯源】

字段

内容

LeetCode

79. Word Search

难度

⭐⭐ 中等

大厂

字节、亚马逊、阿里

考频

⭐⭐⭐

真实面经

字节二面、亚马逊一面

【方案B · 面试追问链】

Q1(基础):写一下。

def exist(board, word):    m, n = len(board), len(board[0])    dirs = [(-1,0),(1,0),(0,-1),(0,1)]    def dfs(i, j, k):        if not (0<=i<m and 0<=j<n) or board[i][j] != word[k]:            return False        if k == len(word)-1:            return True        tmp, board[i][j] = board[i][j], '#'        for dx, dy in dirs:            if dfs(i+dx, j+dy, k+1):                return True        board[i][j] = tmp        return False    for i in range(m):        for j in range(n):            if dfs(i, j, 0):                return True    return False

Q2(变种):单词搜索 II(多个单词)。

用Trie树优化,LeetCode 212。

Q3(优化):如何避免重复搜索?

用visited数组或原地修改。

Q4(变种):返回所有路径坐标。

回溯时记录路径。


47. 数据流的中位数

【方案A · 真题溯源】

字段

内容

LeetCode

295. Find Median from Data Stream

难度

⭐⭐⭐ 困难

大厂

字节、阿里、腾讯

考频

⭐⭐⭐⭐

真实面经

字节三面、阿里三面

【方案B · 面试追问链】

Q1(基础):写一下。

class MedianFinder:    def __init__(self):        import heapq        self.small = []        self.large = []    def addNum(self, num):        heapq.heappush(self.small, -num)        heapq.heappush(self.large, -heapq.heappop(self.small))        if len(self.large) > len(self.small) + 1:            heapq.heappush(self.small, -heapq.heappop(self.large))    def findMedian(self):        if len(self.large) > len(self.small):            return self.large[0]        return (self.large[0] - self.small[0]) / 2

Q2(原理):为什么用两个堆?

大顶堆放较小的一半,小顶堆放较大的一半。

Q3(变种):滑动窗口中位数。

需要懒删除,LeetCode 480。

Q4(变种):如果数据量很大,内存放不下?

分桶统计(近似中位数)。


48. 前K个高频元素

【方案A · 真题溯源】

字段

内容

LeetCode

347. Top K Frequent Elements

难度

⭐⭐ 中等

大厂

字节、腾讯、美团

考频

⭐⭐⭐⭐

真实面经

字节二面、美团二面

【方案B · 面试追问链】

Q1(基础):写一下。

def topKFrequent(nums, k):    from collections import Counter    import heapq    cnt = Counter(nums)    return heapq.nlargest(k, cnt.keys(), key=cnt.get)

Q2(手写):不用heapq,手写堆。

维护一个大小为k的最小堆。

Q3(变种):频率按值排序。

相同频率按数字大小排序。

Q4(变种):用桶排序(O(n))。

频率作为桶下标,逆序遍历。


49. LRU缓存

【方案A · 真题溯源】

字段

内容

LeetCode

146. LRU Cache

难度

⭐⭐ 中等

大厂

字节、阿里、腾讯、微软

考频

⭐⭐⭐⭐⭐

真实面经

字节二面、阿里二面、腾讯二面

【方案B · 面试追问链】

Q1(基础):写一下。

from collections import OrderedDictclass LRUCache:    def __init__(self, capacity):        self.cap = capacity        self.d = OrderedDict()    def get(self, key):        if key not in self.d: return -1        self.d.move_to_end(key)        return self.d[key]    def put(self, key, value):        if key in self.d:            self.d.move_to_end(key)        self.d[key] = value        if len(self.d) > self.cap:            self.d.popitem(last=False)

Q2(要求):手写双向链表 + 哈希。

面试官可能要求不用OrderedDict。

Q3(变种):LFU缓存。

LeetCode 460,更复杂,需要多级链表。

Q4(变种):LRU的多线程安全版本。

加锁或使用并发数据结构。


50. 搜索旋转排序数组

【方案A · 真题溯源】

字段

内容

LeetCode

33. Search in Rotated Sorted Array

难度

⭐⭐ 中等

大厂

字节、阿里、亚马逊

考频

⭐⭐⭐⭐⭐

真实面经

字节二面、阿里二面

【方案B · 面试追问链】

Q1(基础):写一下。

def search(nums, target):    l, r = 0, len(nums)-1    while l <= r:        mid = (l+r)//2        if nums[mid] == target:            return mid        if nums[l] <= nums[mid]:            if nums[l] <= target < nums[mid]:                r = mid - 1            else:                l = mid + 1        else:            if nums[mid] < target <= nums[r]:                l = mid + 1            else:                r = mid - 1    return -1

LeetCode高频面试题速查表及使用建议

旋转数组相关变种题(补充)

Q2(变种):有重复元素(LeetCode 81)。

当nums[l]==nums[mid]时,l++。

Q3(变种):找旋转数组中的最小值(LeetCode 153)。

二分找分界点。

Q4(变种):找旋转数组中的最小值 II(有重复)

当相等时

抱歉,评论功能暂时关闭!