「算法武林大会」的故事

6 阅读5分钟

好的!我用一个「算法武林大会」的故事,结合 10 道高频算法题,帮你快速掌握面试必考题!

一、故事背景:算法武林大会

场景
你参加一场算法武林大会,要通过 10 道关卡才能成为武林盟主!每道关卡对应一种经典算法题。

二、高频算法题分类及解法

1. 二分查找(LeetCode 704)

关卡描述
在排序数组 [1,3,5,7,9] 中找到数字 7 的位置。

解题思路

  • 每次将数组分成两半,比较中间值与目标值的大小,缩小查找范围。

Java 代码

java

public int search(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;  // 防止溢出
        if (nums[mid] == target) return mid;
        if (nums[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;  // 未找到
}

2. 双指针(LeetCode 11)

关卡描述
给定数组 [1,8,6,2,5,4,8,3,7],找到能容纳最多水的两个柱子之间的区域。

解题思路

  • 左右指针分别指向数组两端,每次移动较短的柱子,计算面积并更新最大值。

Java 代码

java

public int maxArea(int[] height) {
    int left = 0, right = height.length - 1;
    int maxArea = 0;
    while (left < right) {
        int area = Math.min(height[left], height[right]) * (right - left);
        maxArea = Math.max(maxArea, area);
        if (height[left] < height[right]) left++;
        else right--;
    }
    return maxArea;
}

3. 滑动窗口(LeetCode 3)

关卡描述
找到字符串 "abcabcbb" 中最长无重复字符的子串长度。

解题思路

  • 用哈希表记录字符出现的位置,右指针扩展窗口,左指针在重复时收缩。

Java 代码

java

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

4. 回溯算法(LeetCode 78)

关卡描述
求数组 [1,2,3] 的所有子集。

解题思路

  • 递归遍历每个元素,选择或不选择该元素,生成所有可能的子集。

Java 代码

java

public List<List<Integer>> subsets(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    backtrack(nums, 0, new ArrayList<>(), result);
    return result;
}

private void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> result) {
    result.add(new ArrayList<>(path));  // 保存当前子集
    for (int i = start; i < nums.length; i++) {
        path.add(nums[i]);  // 选择当前元素
        backtrack(nums, i + 1, path, result);  // 递归
        path.remove(path.size() - 1);  // 撤销选择
    }
}

5. 动态规划(LeetCode 53)

关卡描述
求数组 [-2,1,-3,4,-1,2,1,-5,4] 的最大子数组和。

解题思路

  • 定义 dp[i] 为以第 i 个元素结尾的最大子数组和,状态转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])

Java 代码

java

public int maxSubArray(int[] nums) {
    int n = nums.length;
    int[] dp = new int[n];
    dp[0] = nums[0];
    int max = dp[0];
    for (int i = 1; i < n; i++) {
        dp[i] = Math.max(nums[i], dp[i-1] + nums[i]);
        max = Math.max(max, dp[i]);
    }
    return max;
}

6. 二叉树遍历(LeetCode 94)

关卡描述
中序遍历二叉树 [1,null,2,3]

解题思路

  • 递归遍历左子树 → 访问根节点 → 递归遍历右子树。

Java 代码

java

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    inorder(root, result);
    return result;
}

private void inorder(TreeNode root, List<Integer> result) {
    if (root == null) return;
    inorder(root.left, result);  // 左
    result.add(root.val);        // 根
    inorder(root.right, result); // 右
}

7. 图的 BFS(LeetCode 102)

关卡描述
层序遍历二叉树 [3,9,20,null,null,15,7]

解题思路

  • 用队列实现 BFS,每层节点入队,处理完一层后将子节点入队。

Java 代码

java

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;
    
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);
    
    while (!queue.isEmpty()) {
        int levelSize = queue.size();
        List<Integer> currentLevel = new ArrayList<>();
        for (int i = 0; i < levelSize; i++) {
            TreeNode node = queue.poll();
            currentLevel.add(node.val);
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
        result.add(currentLevel);
    }
    return result;
}

8. 贪心算法(LeetCode 55)

关卡描述
数组 [2,3,1,1,4],每个元素表示最大跳跃长度,判断是否能跳到最后一个位置。

解题思路

  • 维护一个最远可达位置 maxReach,遍历数组更新 maxReach,如果 maxReach 能到达末尾则返回 true

Java 代码

java

public boolean canJump(int[] nums) {
    int maxReach = 0;
    for (int i = 0; i < nums.length; i++) {
        if (i > maxReach) return false;  // 无法到达当前位置
        maxReach = Math.max(maxReach, i + nums[i]);
        if (maxReach >= nums.length - 1) return true;  // 已到达末尾
    }
    return false;
}

9. 排序算法(LeetCode 215)

关卡描述
找到数组 [3,2,1,5,6,4] 的第 2 大元素。

解题思路

  • 用快速选择算法(类似快排),平均时间复杂度 O (n)。

Java 代码

java

public int findKthLargest(int[] nums, int k) {
    int targetIndex = nums.length - k;  // 第 k 大 = 第 (n-k) 小
    return quickSelect(nums, 0, nums.length - 1, targetIndex);
}

private int quickSelect(int[] nums, int left, int right, int targetIndex) {
    if (left == right) return nums[left];
    
    int pivotIndex = partition(nums, left, right);
    if (pivotIndex == targetIndex) return nums[pivotIndex];
    else if (pivotIndex > targetIndex) return quickSelect(nums, left, pivotIndex - 1, targetIndex);
    else return quickSelect(nums, pivotIndex + 1, right, targetIndex);
}

private int partition(int[] nums, int left, int right) {
    int pivot = nums[right];
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (nums[j] <= pivot) {
            i++;
            swap(nums, i, j);
        }
    }
    swap(nums, i + 1, right);
    return i + 1;
}

private void swap(int[] nums, int i, int j) {
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

10. 位运算(LeetCode 136)

关卡描述
数组 [4,1,2,1,2] 中,只有一个元素只出现一次,其他元素都出现两次,找出这个元素。

解题思路

  • 利用异或运算 ^ 的性质:a ^ a = 0a ^ 0 = a,遍历数组异或所有元素。

Java 代码

java

public int singleNumber(int[] nums) {
    int result = 0;
    for (int num : nums) {
        result ^= num;  // 异或运算
    }
    return result;
}

三、高频算法题分类总结

类别核心思想典型题目
二分查找分治,缩小搜索范围有序数组找目标值
双指针两端向中间逼近或快慢指针盛最多水的容器、链表环
滑动窗口动态调整窗口大小无重复字符的最长子串
回溯算法递归遍历所有可能子集、排列组合
动态规划拆分子问题,记录中间结果最大子数组和、背包问题
树与图遍历BFS/DFS二叉树层序遍历、图的最短路径
贪心算法每步选最优,局部最优到全局跳跃游戏、活动选择
排序与选择快排、归并、堆排序第 K 大元素、数组排序
位运算利用位运算特性只出现一次的数字

四、面试准备建议

  1. 掌握基础算法:二分、双指针、DFS/BFS、动态规划。

  2. 多刷题:从高频题开始,推荐 LeetCode 前 200 题。

  3. 分析复杂度:时间 / 空间复杂度是面试重点。

  4. 手写代码:练习白板编程,注意边界条件和代码风格。

记忆口诀

  • 算法武林有十招,二分双指针滑动窗,回溯动规加贪心,树图排序位运算!