跳转至

大厂面试专项讲解 - 字节/阿里/腾讯真题

重要性:⭐⭐⭐⭐⭐ 目标:大厂面试准备 内容:真题分类 + 解题思路 + 面试技巧


📚 目录

  1. 字节跳动面试真题
  2. 阿里巴巴面试真题
  3. 腾讯面试真题
  4. 面试技巧与策略
  5. 高频考点总结

字节跳动面试真题

面试特点

  • 算法难度:⭐⭐⭐⭐⭐(偏难)
  • 考察重点:动态规划、图算法、系统设计
  • 编程语言:主要使用Python/Go/Java
  • 时间限制:通常45-60分钟

题目1:LRU缓存机制 ⭐⭐⭐⭐⭐

题目链接LeetCode 146

难度:中等

出现频率:⭐⭐⭐⭐⭐(字节超高频)

问题描述: 请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。实现 LRUCache 类: - LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存 - int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 - void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value;如果不存在,则向缓存中插入该组 key-value。如果插入操作导致关键字数量超过 capacity,则应该逐出最久未使用的关键字。


解法:哈希表 + 双向链表(推荐)⭐

Python
class DLinkedNode:
    """双向链表节点"""
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    """
    LRU缓存
    时间:O(1) get和put
    空间:O(capacity)
    """

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.cache = {}  # 哈希表:key -> 节点

        # 创建伪头部和伪尾部节点
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head

    def _remove_node(self, node):
        """移除节点"""
        node.prev.next = node.next
        node.next.prev = node.prev

    def _add_to_head(self, node):
        """添加节点到头部"""
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def _move_to_head(self, node):
        """移动节点到头部"""
        self._remove_node(node)
        self._add_to_head(node)

    def get(self, key: int) -> int:
        """获取值"""
        if key not in self.cache:
            return -1

        # 获取节点,移动到头部
        node = self.cache[key]
        self._move_to_head(node)

        return node.value

    def put(self, key: int, value: int) -> None:
        """放入值"""
        if key in self.cache:
            # 更新值,移动到头部
            node = self.cache[key]
            node.value = value
            self._move_to_head(node)
        else:
            # 创建新节点
            node = DLinkedNode(key, value)
            self.cache[key] = node
            self._add_to_head(node)

            # 检查容量
            if len(self.cache) > self.capacity:
                # 移除尾部节点(最久未使用)
                removed = self.tail.prev
                self._remove_node(removed)
                del self.cache[removed.key]

执行过程演示

Text Only
LRUCache lRUCache = new LRUCache(2);

put(1, 1): cache = {1:1}
         链表: head <-> (1,1) <-> tail

put(2, 2): cache = {1:1, 2:2}
         链表: head <-> (2,2) <-> (1,1) <-> tail

get(1):   返回 1
         cache = {1:1, 2:2}
         链表: head <-> (1,1) <-> (2,2) <-> tail
         (1被访问,移到头部)

put(3, 3): 容量超限,移除(2,2)
         cache = {1:1, 3:3}
         链表: head <-> (3,3) <-> (1,1) <-> tail

get(2):   返回 -1(未找到)

put(4, 4): 容量超限,移除(1,1)
         cache = {3:3, 4:4}
         链表: head <-> (4,4) <-> (3,3) <-> tail

复杂度分析: - 时间:O(1),get和put都是常数时间 - 空间:O(capacity),哈希表和链表都存储capacity个元素

面试要点: 1. 为什么用双向链表? - O(1)时间删除节点(需要前驱节点)

  1. 为什么用哈希表
  2. O(1)时间查找节点

  3. 为什么需要伪头尾节点

  4. 简化边界条件处理

题目2:四数之和 ⭐⭐⭐⭐

题目链接LeetCode 18

难度:中等

出现频率:⭐⭐⭐⭐

问题描述: 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 abcd,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。


解法:排序 + 双指针 ⭐

Python
def four_sum(nums, target):
    """
    四数之和
    时间:O(n³)
    空间:O(1)
    """
    nums.sort()
    n = len(nums)
    result = []

    for i in range(n - 3):
        # 跳过重复
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        for j in range(i + 1, n - 2):
            # 跳过重复
            if j > i + 1 and nums[j] == nums[j - 1]:
                continue

            # 双指针
            left, right = j + 1, n - 1

            while left < right:
                current_sum = nums[i] + nums[j] + nums[left] + nums[right]

                if current_sum == target:
                    result.append([nums[i], nums[j], 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 current_sum < target:
                    left += 1
                else:
                    right -= 1

    return result

优化技巧: 1. 提前终止:如果最小的4个数之和 > target,可以提前返回 2. 剪枝:如果当前数 + 最大的3个数 < target,跳过当前数


题目3:接雨水 ⭐⭐⭐⭐⭐

题目链接LeetCode 42

难度:困难

出现频率:⭐⭐⭐⭐⭐(字节必考)

问题描述: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。


解法1:双指针(推荐)⭐

Python
def trap(height):
    """
    接雨水(双指针)
    时间:O(n)
    空间:O(1)
    """
    if not height:
        return 0

    left, right = 0, len(height) - 1
    left_max, right_max = height[left], height[right]
    water = 0

    while left < right:
        if left_max < right_max:
            left += 1
            left_max = max(left_max, height[left])
            water += left_max - height[left]
        else:
            right -= 1
            right_max = max(right_max, height[right])
            water += right_max - height[right]

    return water

执行过程演示

Text Only
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]

图示:
   █ █ █
 █ █ █ █ █ █
█ █ █ █ █ █ █ █
0 1 0 2 1 0 1 3 2 1 2 1

初始:left=0, right=11, left_max=0, right_max=1

第1次迭代:
  - left_max(0) < right_max(1)
  - left++ → left=1
  - left_max = max(0, 1) = 1
  - water += 1 - 1 = 0

第2次迭代:
  - left_max(1) >= right_max(1)
  - right-- → right=10
  - right_max = max(1, 2) = 2
  - water += 2 - 2 = 0

...(继续这个过程)

最终结果:6

核心思想: - 水量取决于左右两边较小的最大值 - 维护left_max和right_max - 总是移动较小的一侧


解法2:动态规划

Python
def trap_dp(height):
    """
    接雨水(动态规划)
    时间:O(n)
    空间:O(n)
    """
    if not height:
        return 0

    n = len(height)

    # left_max[i] = height[0:i]的最大值
    left_max = [0] * n
    left_max[0] = height[0]
    for i in range(1, n):
        left_max[i] = max(left_max[i - 1], height[i])

    # right_max[i] = height[i:n]的最大值
    right_max = [0] * n
    right_max[n - 1] = height[n - 1]
    for i in range(n - 2, -1, -1):
        right_max[i] = max(right_max[i + 1], height[i])

    # 计算水量
    water = 0
    for i in range(n):
        water += min(left_max[i], right_max[i]) - height[i]

    return water

题目4:最长有效括号 ⭐⭐⭐⭐⭐

题目链接LeetCode 32

难度:困难

出现频率:⭐⭐⭐⭐⭐

问题描述: 给你一个只包含 '('')' 的字符串,找出最长有效(正确连续)括号子串的长度。


解法:栈(推荐)⭐

Python
def longest_valid_parentheses(s):
    """
    最长有效括号(栈)
    时间:O(n)
    空间:O(n)
    """
    max_length = 0
    stack = [-1]  # 初始化:-1作为起始位置

    for i, char in enumerate(s):
        if char == '(':
            stack.append(i)
        else:
            # 弹出栈顶
            stack.pop()

            if not stack:
                # 栈空,压入当前索引作为新的起始位置
                stack.append(i)
            else:
                # 计算有效长度
                current_length = i - stack[-1]
                max_length = max(max_length, current_length)

    return max_length

执行过程演示

Text Only
输入:s = ")()())"

初始:stack = [-1], max_length = 0

i=0, char=')':
  - stack.pop() → stack = []
  - 栈空,stack.append(0)
  - stack = [0]

i=1, char='(':
  - stack.append(1)
  - stack = [0, 1]

i=2, char=')':
  - stack.pop() → stack = [0]
  - 栈不空
  - length = 2 - 0 = 2
  - max_length = 2

i=3, char='(':
  - stack.append(3)
  - stack = [0, 3]

i=4, char=')':
  - stack.pop() → stack = [0]
  - 栈不空
  - length = 4 - 0 = 4
  - max_length = 4

i=5, char=')':
  - stack.pop() → stack = []
  - 栈空,stack.append(5)
  - stack = [5]

结果:4
子串:"()()"


题目5:滑动窗口最大值 ⭐⭐⭐⭐⭐

题目链接LeetCode 239

难度:困难

出现频率:⭐⭐⭐⭐⭐

问题描述: 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。


解法:单调队列(推荐)⭐

Python
from collections import deque

def max_sliding_window(nums, k):
    """
    滑动窗口最大值(单调队列)
    时间:O(n)
    空间:O(k)
    """
    if not nums or k <= 0:
        return []

    # 单调递减队列:存储索引
    deque_idx = deque()
    result = []

    for i, num in enumerate(nums):
        # 移除超出窗口的元素
        while deque_idx and deque_idx[0] <= i - k:
            deque_idx.popleft()

        # 移除比当前元素小的元素(单调递减)
        while deque_idx and nums[deque_idx[-1]] < num:  # 负索引:从末尾倒数访问元素
            deque_idx.pop()

        # 添加当前元素索引
        deque_idx.append(i)

        # 记录窗口最大值
        if i >= k - 1:
            result.append(nums[deque_idx[0]])

    return result

执行过程演示

Text Only
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3

i=0, num=1:
  - deque = [0]
  - i < k-1,不记录

i=1, num=3:
  - 3 > 1,移除0
  - deque = [1]
  - i < k-1,不记录

i=2, num=-1:
  - deque = [1, 2]
  - i >= k-1,记录nums[1]=3
  - result = [3]

i=3, num=-3:
  - 移除索引0(无)
  - deque = [1, 2, 3]
  - 记录nums[1]=3
  - result = [3, 3]

i=4, num=5:
  - 移除索引1(超出窗口)
  - 5 > -3, -1, 3,全部移除
  - deque = [4]
  - 记录nums[4]=5
  - result = [3, 3, 5]

i=5, num=3:
  - deque = [4, 5]
  - 记录nums[4]=5
  - result = [3, 3, 5, 5]

i=6, num=6:
  - 6 > 5, 3,全部移除
  - deque = [6]
  - 记录nums[6]=6
  - result = [3, 3, 5, 5, 6]

i=7, num=7:
  - 7 > 6,移除
  - deque = [7]
  - 记录nums[7]=7
  - result = [3, 3, 5, 5, 6, 7]

结果:[3, 3, 5, 5, 6, 7]


阿里巴巴面试真题

面试特点

  • 算法难度:⭐⭐⭐⭐
  • 考察重点:数据库、分布式系统、设计模式
  • 编程语言:主要使用Java
  • 时间限制:通常45-60分钟

题目6:反转链表II ⭐⭐⭐⭐

题目链接LeetCode 92

难度:中等

出现频率:⭐⭐⭐⭐

问题描述: 给你单链表的头指针 head 和两个整数 leftright,其中 left <= right。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。


解法:迭代(推荐)⭐

Python
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverse_between(head, left, right):
    """
    反转链表II
    时间:O(n)
    空间:O(1)
    """
    # 创建虚拟头节点
    dummy = ListNode(0)
    dummy.next = head

    # 第1步:找到left前一个节点
    prev = dummy
    for _ in range(left - 1):
        prev = prev.next

    # 第2步:反转从left到right的节点
    current = prev.next
    reverse_prev = None

    for _ in range(right - left + 1):
        next_node = current.next
        current.next = reverse_prev
        reverse_prev = current
        current = next_node

    # 第3步:连接反转后的链表
    prev.next.next = current
    prev.next = reverse_prev

    return dummy.next

执行过程演示

Text Only
输入:head = [1,2,3,4,5], left = 2, right = 4

初始链表:1 -> 2 -> 3 -> 4 -> 5

第1步:找到left前一个节点
  - prev指向节点1

第2步:反转[2,3,4]
  - current = 2
  - 第1次:2.next = None,    reverse_prev = 2
  - 第2次:3.next = 2,       reverse_prev = 3
  - 第3次:4.next = 3->2,    reverse_prev = 4

第3步:连接
  - prev.next.next = current  (2.next = 5)
  - prev.next = reverse_prev  (1.next = 4)

结果:1 -> 4 -> 3 -> 2 -> 5 ✓


题目7:二叉树的中序遍历 ⭐⭐⭐

题目链接LeetCode 94

难度:简单

出现频率:⭐⭐⭐⭐

问题描述: 给定一个二叉树的根节点 root,返回它的中序遍历。


解法1:递归

Python
def inorder_traversal(root):
    """
    中序遍历(递归)
    时间:O(n)
    空间:O(h)
    """
    result = []

    def inorder(node):
        if not node:
            return

        inorder(node.left)
        result.append(node.val)
        inorder(node.right)

    inorder(root)
    return result

解法2:迭代(推荐)⭐

Python
def inorder_traversal_iterative(root):
    """
    中序遍历(迭代)
    时间:O(n)
    空间:O(h)
    """
    result = []
    stack = []
    current = root

    while current or stack:
        # 一直向左走
        while current:
            stack.append(current)
            current = current.left

        # 访问节点
        current = stack.pop()
        result.append(current.val)

        # 转向右子树
        current = current.right

    return result

题目8: Pow(x, n) ⭐⭐⭐⭐

题目链接LeetCode 50

难度:中等

出现频率:⭐⭐⭐⭐

问题描述: 实现 pow(x, n),即计算 xn 次幂函数。


解法:快速幂(推荐)⭐

Python
def my_pow(x, n):
    """
    Pow(x, n)(快速幂)
    时间:O(log n)
    空间:O(1)
    """
    def fast_pow(x, n):
        if n == 0:
            return 1.0

        half = fast_pow(x, n // 2)

        if n % 2 == 0:
            return half * half
        else:
            return half * half * x

    if n < 0:
        x = 1 / x
        n = -n

    return fast_pow(x, n)

数学原理

Text Only
x^n = {
    (x^(n/2))^2,           n为偶数
    (x^(n/2))^2 * x,       n为奇数
}

执行过程演示

Text Only
计算:2^10

fast_pow(2, 10):
  - half = fast_pow(2, 5)

  fast_pow(2, 5):
    - half = fast_pow(2, 2)

    fast_pow(2, 2):
      - half = fast_pow(2, 1)

      fast_pow(2, 1):
        - half = fast_pow(2, 0) = 1
        - 1是奇数,返回 1*1*2 = 2

      - 2是偶数,返回 2*2 = 4

    - 5是奇数,返回 4*4*2 = 32

  - 10是偶数,返回 32*32 = 1024

结果:1024 ✓


题目9:最长公共前缀 ⭐⭐⭐

题目链接LeetCode 14

难度:简单

出现频率:⭐⭐⭐⭐

问题描述: 编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""


解法:垂直扫描(推荐)⭐

Python
def longest_common_prefix(strs):
    """
    最长公共前缀(垂直扫描)
    时间:O(m * n),m是最短字符串长度,n是字符串数量
    空间:O(1)
    """
    if not strs:
        return ""

    # 找到最短字符串
    min_str = min(strs, key=len)

    for i, char in enumerate(min_str):
        # 检查所有字符串的第i个字符
        for s in strs:
            if s[i] != char:
                return min_str[:i]

    return min_str

题目10:括号生成 ⭐⭐⭐⭐

题目链接LeetCode 22

难度:中等

出现频率:⭐⭐⭐⭐⭐

问题描述: 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。


解法:回溯(推荐)⭐

Python
def generate_parenthesis(n):
    """
    括号生成(回溯)
    时间:O(4^n / sqrt(n))
    空间:O(n)
    """
    result = []

    def backtrack(current, left, right):
        """
        current: 当前生成的字符串
        left: 左括号数量
        right: 右括号数量
        """
        # 终止条件:生成了2n个字符
        if len(current) == 2 * n:
            result.append(current)
            return

        # 添加左括号(如果左括号数量 < n)
        if left < n:
            backtrack(current + '(', left + 1, right)

        # 添加右括号(如果右括号数量 < 左括号数量)
        if right < left:
            backtrack(current + ')', left, right + 1)

    backtrack('', 0, 0)
    return result

执行过程演示

Text Only
输入:n = 2

递归树:
                    ""
                  /     \
             "(1,0)"    (不能添加")")
              /    \
       "(2,0)"      "(1,1)"
         /            \
    "(2,1)"           "(2,1)" → "()()"
        ↓                  ↓
    "(2,2)" → "(())"  "(2,2)" → "()()"

结果:["(())", "()()"]


腾讯面试真题

面试特点

  • 算法难度:⭐⭐⭐⭐
  • 考察重点:网络编程、操作系统、数据结构
  • 编程语言:C++/Java/Go
  • 时间限制:通常45-60分钟

题目11:全排列 ⭐⭐⭐⭐⭐

题目链接LeetCode 46

难度:中等

出现频率:⭐⭐⭐⭐⭐

问题描述: 给定一个不含重复数字的数组 nums,返回其所有可能的全排列。你可以按任意顺序返回答案。


解法1:回溯 + used数组(推荐)⭐

Python
def permute(nums):
    """
    全排列(回溯)
    时间:O(n! * n)
    空间:O(n)
    """
    result = []
    n = len(nums)
    used = [False] * n

    def backtrack(current):
        # 终止条件:生成了n个数字
        if len(current) == n:
            result.append(current[:])
            return

        for i in range(n):
            if not used[i]:
                # 选择
                used[i] = True
                current.append(nums[i])

                # 递归
                backtrack(current)

                # 撤销选择
                current.pop()
                used[i] = False

    backtrack([])
    return result

解法2:交换法(优化)⭐⭐

Python
def permute_swap(nums):
    """
    全排列(交换法)
    时间:O(n! * n)
    空间:O(1)(不包括输出)
    """
    result = []

    def backtrack(start):
        # 终止条件
        if start == len(nums):
            result.append(nums[:])
            return

        for i in range(start, len(nums)):
            # 交换
            nums[start], nums[i] = nums[i], nums[start]

            # 递归
            backtrack(start + 1)

            # 撤销交换
            nums[start], nums[i] = nums[i], nums[start]

    backtrack(0)
    return result

执行过程演示

Text Only
输入:nums = [1, 2, 3]

递归树:
                [1,2,3]
              /    |    \
         swap(0,0) swap(0,1) swap(0,2)
            |        |        |
          [1,2,3] [2,1,3] [3,2,1]
           /  \      /  \      /  \
      [1,2,3] [1,3,2] [2,1,3] [2,3,1] [3,2,1] [3,1,2]

结果:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]


题目12:子集 ⭐⭐⭐⭐⭐

题目链接LeetCode 78

难度:中等

出现频率:⭐⭐⭐⭐⭐

问题描述: 给你一个整数数组 nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。你可以按任意顺序返回解集。


解法:回溯(推荐)⭐

Python
def subsets(nums):
    """
    子集(回溯)
    时间:O(2^n * n)
    空间:O(n)
    """
    result = []

    def backtrack(start, current):
        # 每次递归都记录当前子集
        result.append(current[:])

        # 从start开始,避免重复
        for i in range(start, len(nums)):
            # 选择nums[i]
            current.append(nums[i])

            # 递归(从i+1开始,避免重复使用)
            backtrack(i + 1, current)

            # 撤销选择
            current.pop()

    backtrack(0, [])
    return result

执行过程演示

Text Only
输入:nums = [1, 2, 3]

递归树:
                    []
              /      |     \
            [1]     [2]    [3]
           /  \       |
        [1,2] [1,3] [2,3]
         /
      [1,2,3]

结果:[[], [1], [2], [3], [1,2], [1,3], [2,3], [1,2,3]]


题目13:组合 ⭐⭐⭐⭐

题目链接LeetCode 77

难度:中等

出现频率:⭐⭐⭐⭐⭐

问题描述: 给定两个整数 nk,返回 1...n 中所有可能的 k 个数的组合。


解法:回溯(推荐)⭐

Python
def combine(n, k):
    """
    组合(回溯)
    时间:O(C(n,k) * k)
    空间:O(k)
    """
    result = []

    def backtrack(start, current):
        # 终止条件:找到k个数
        if len(current) == k:
            result.append(current[:])  # 切片操作:[start:end:step]提取子序列
            return

        # 从start到n
        # 剪枝优化:i最多到 n - (k - len(current)) + 1
        for i in range(start, n - (k - len(current)) + 2):
            # 选择i
            current.append(i)

            # 递归(从i+1开始,保证递增顺序)
            backtrack(i + 1, current)

            # 撤销选择
            current.pop()

    backtrack(1, [])
    return result

剪枝优化图解

Text Only
n=4, k=2

不剪枝:
i的范围:[1, 4]
  - i=1: [1,2], [1,3], [1,4]
  - i=2: [2,3], [2,4]
  - i=3: [3,4]
  - i=4: [4,?](无效,无法凑够2个)

剪枝后:
i的范围:[1, 3](n - k + 2 = 4 - 2 + 2 = 4,但range(1, 4)=[1,2,3])
  - i=1: [1,2], [1,3], [1,4]
  - i=2: [2,3], [2,4]
  - i=3: [3,4]
  (跳过了i=4的无效情况)


题目14:单词搜索 ⭐⭐⭐⭐

题目链接LeetCode 79

难度:中等

出现频率:⭐⭐⭐⭐

问题描述: 给定一个 m x n 二维字符网格 board 和一个字符串单词 word。如果 word 存在于网格中,返回 true;否则,返回 false。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中"相邻"单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。


解法:回溯 + DFS(推荐)⭐

Python
def exist(board, word):
    """
    单词搜索
    时间:O(m * n * 4^L),L是word长度
    空间:O(L)
    """
    if not board or not board[0]:
        return False

    m, n = len(board), len(board[0])

    def dfs(i, j, index):
        """
        i, j: 当前位置
        index: word的索引
        """
        # 终止条件:找到完整单词
        if index == len(word):
            return True

        # 边界检查 + 字符匹配
        if i < 0 or i >= m or j < 0 or j >= n:
            return False
        if board[i][j] != word[index]:
            return False

        # 标记已访问
        temp = board[i][j]
        board[i][j] = '#'

        # 向四个方向搜索
        found = (dfs(i + 1, j, index + 1) or
                 dfs(i - 1, j, index + 1) or
                 dfs(i, j + 1, index + 1) or
                 dfs(i, j - 1, index + 1))

        # 恢复
        board[i][j] = temp

        return found

    # 从每个位置开始搜索
    for i in range(m):
        for j in range(n):
            if dfs(i, j, 0):
                return True

    return False

题目15:岛屿数量 ⭐⭐⭐⭐⭐

题目链接LeetCode 200

难度:中等

出现频率:⭐⭐⭐⭐⭐(腾讯必考)

问题描述: 给你一个由 '1'(陆地)和 '0'(水)组成的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。


解法:DFS(推荐)⭐

Python
def num_islands(grid):
    """
    岛屿数量(DFS)
    时间:O(m * n)
    空间:O(m * n)
    """
    if not grid or not grid[0]:
        return 0

    m, n = len(grid), len(grid[0])
    count = 0

    def dfs(i, j):
        """标记相连的陆地"""
        # 边界检查
        if i < 0 or i >= m or j < 0 or j >= n:
            return
        if grid[i][j] != '1':
            return

        # 标记为已访问
        grid[i][j] = '2'

        # 向四个方向扩展
        dfs(i + 1, j)
        dfs(i - 1, j)
        dfs(i, j + 1)
        dfs(i, j - 1)

    # 遍历整个网格
    for i in range(m):
        for j in range(n):
            if grid[i][j] == '1':
                # 发现新岛屿
                count += 1
                # 标记整个岛屿
                dfs(i, j)

    return count

执行过程演示

Text Only
输入:
grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]

遍历:
(0,0) = '1':发现岛屿1,标记(0,0),(0,1),(1,0),(1,1)
grid = [
  ["2","2","0","0","0"],
  ["2","2","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]

(2,2) = '1':发现岛屿2,标记(2,2)
grid = [
  ["2","2","0","0","0"],
  ["2","2","0","0","0"],
  ["0","0","2","0","0"],
  ["0","0","0","1","1"]
]

(3,3) = '1':发现岛屿3,标记(3,3),(3,4)
grid = [
  ["2","2","0","0","0"],
  ["2","2","0","0","0"],
  ["0","0","2","0","0"],
  ["0","0","0","2","2"]
]

结果:3 ✓


面试技巧与策略

1. 算法面试流程

Text Only
第1步:理解题意(2-3分钟)
  - 明确输入输出
  - 确认边界条件
  - 举例说明

第2步:分析思路(3-5分钟)
  - 暴力解法
  - 优化思路
  - 时间空间复杂度

第3步:编写代码(15-20分钟)
  - 先写核心逻辑
  - 处理边界情况
  - 添加必要注释

第4步:测试验证(3-5分钟)
  - 正常情况
  - 边界情况
  - 特殊情况

第5步:优化讨论(2-3分钟)
  - 是否有更优解
  - 其他解法

2. 沟通技巧

✅ 好的沟通

  • "让我先理解一下题目..."
  • "我的思路是..."
  • "时间复杂度是O(n),因为..."
  • "让我用个例子验证一下..."

❌ 不好的沟通

  • (直接开始写代码,不说话)
  • (写完代码不解释)
  • "我忘了..."
  • (遇到问题长时间沉默)

3. 代码风格

✅ 好的风格

Python
def two_sum(nums, target):
    """
    两数之和
    时间:O(n)
    空间:O(n)
    """
    # 创建哈希表
    num_to_index = {}

    for i, num in enumerate(nums):
        complement = target - num

        # 检查补数
        if complement in num_to_index:
            return [num_to_index[complement], i]

        num_to_index[num] = i

    return []

❌ 不好的风格

Python
def f(a,b):
    d={}
    for i,x in enumerate(a):  # enumerate同时获取索引和值
        y=b-x
        if y in d:return[d[y],i]
        d[x]=i
    return[]

4. 常见陷阱

陷阱1:边界条件

Python
# ❌ 忘记处理空数组
def max_sub_array(nums):
    return max(nums)  # 空数组会报错

# ✅ 正确处理
def max_sub_array(nums):
    if not nums:
        return 0
    return max(nums)

陷阱2:整数溢出

Python
# ❌ 可能溢出
def multiply(a, b):
    return a * b  # 大数可能溢出

# ✅ 使用模运算
def multiply(a, b, mod=10**9 + 7):
    return (a * b) % mod

陷阱3:索引越界

Python
# ❌ 可能越界
def get_next(arr, i):
    return arr[i + 1]  # 最后一个元素会越界

# ✅ 边界检查
def get_next(arr, i):
    if i + 1 < len(arr):
        return arr[i + 1]
    return None

高频考点总结

数据结构高频考点

1. 数组

  • ✅ 双指针(两数之和、合并区间)
  • ✅ 滑动窗口(最小覆盖子串)
  • ✅ 前缀和(和为K的子数组)

2. 链表

  • ✅ 快慢指针(环形链表、链表中点)
  • ✅ 虚拟头节点(简化操作)
  • ✅ 反转链表(迭代、递归)

3. 树

  • ✅ 递归遍历(前、中、后序)
  • ✅ 迭代遍历(栈、队列)
  • ✅ BST性质(左<根<右)

4. 图

  • ✅ BFS(最短路径)
  • ✅ DFS(连通性)
  • ✅ 拓扑排序(课程表)

算法高频考点

1. 动态规划

  • ✅ 线性DP(爬楼梯、打家劫舍)
  • ✅ 背包DP(零钱兑换)
  • ✅ 区间DP(最长回文子串)
  • ✅ 状态压缩(位运算)

2. 贪心

  • ✅ 区间调度(活动选择)
  • ✅ 跳跃游戏
  • ✅ 分配问题

3. 回溯

  • ✅ 排列组合(全排列、组合)
  • ✅ 搜索问题(N皇后、单词搜索)

4. 二分查找

  • ✅ 标准二分(搜索插入位置)
  • ✅ 二分答案(最小化最大值)
  • ✅ 旋转数组(搜索旋转排序数组)

刷题优先级

必刷题(⭐⭐⭐⭐⭐)

  1. 两数之和
  2. 买卖股票的最佳时机
  3. 最大子数组和
  4. 三数之和
  5. 反转链表
  6. 合并两个有序链表
  7. 二叉树的最大深度
  8. 爬楼梯
  9. 打家劫舍
  10. 零钱兑换
  11. 最长递增子序列
  12. 课程表
  13. 括号生成
  14. 全排列
  15. 子集
  16. 单词搜索
  17. 岛屿数量
  18. LRU缓存
  19. 接雨水
  20. 滑动窗口最大值

高频题(⭐⭐⭐⭐)

  1. 最长公共前缀
  2. 验证二叉搜索树
  3. 层序遍历
  4. Pow(x, n)
  5. 反转链表II
  6. 四数之和
  7. 最长有效括号
  8. 组合
  9. 二叉树展开为链表
  10. 被围绕的区域

📝 总结

三大厂特点对比

公司 难度 重点 语言 风格
字节跳动 ⭐⭐⭐⭐⭐ DP、图、系统设计 Python/Go 偏难、创新
阿里巴巴 ⭐⭐⭐⭐ 数据库、分布式 Java 稳重、深入
腾讯 ⭐⭐⭐⭐ 网络、OS、数据结构 C++/Java 实用、基础

备考建议

1. 按公司准备

  • 字节:重点刷DP和图算法
  • 阿里:重点刷基础数据结构
  • 腾讯:重点刷网络和OS相关

2. 时间分配

  • 基础(1个月):数据结构 + 基础算法
  • 进阶(1个月):DP + 图算法 + 回溯
  • 冲刺(2周):公司真题 + 模拟面试

3. 模拟练习

  • 每天限时做2-3题
  • 模拟面试环境
  • 记录错题和思路

祝你面试顺利,拿到心仪的Offer!💪🎉