🔍 LeetCode 4:两个正序数组的中位数 —— 一道把「二分查找」用到极致的经典难题

10 阅读3分钟

关键词:二分查找 / 分割思想 / O(log(m+n))
难度评价:Hard(但理解后非常优雅)


一、题目回顾

给定两个正序数组 nums1nums2,长度分别为 mn

nums1 = [1,3]
nums2 = [2]

合并后:

[1,2,3] → 中位数 = 2

⚠️ 关键要求

算法时间复杂度必须是 O(log (m+n))

这意味着:

  • ❌ 不能直接合并数组
  • ❌ 不能线性扫描
  • 必须用二分查找

二、暴力思路为什么不行?

1️⃣ 合并数组 + 排序

O((m+n) log (m+n))

👉 不满足题目要求


2️⃣ 双指针合并到中位数位置

O(m+n)

👉 还是不行


三、真正的突破点:不要合并数组

💡 核心思想

我们不需要真正合并数组
只需要找到一个“分割点”,
让左右两边数量相等,并且整体有序


四、把问题转成「分割问题」

设:

  • nums1 中切一刀:i
  • nums2 中切一刀:j
nums1: | left_A | right_A |
nums2: | left_B | right_B |

目标状态

1️⃣ 左边元素个数 = 右边元素个数(或多一个)
2️⃣ 左边所有元素 ≤ 右边所有元素


五、为什么可以用二分查找?

我们只在 较短数组 nums1 上二分:

i[0, m]
j = (m + n + 1) / 2 - i

为什么这样做?

  • 搜索空间是有序的
  • i 增大,j 必然减小
  • 条件判断具有单调性

👉 完全符合二分查找的使用场景


六、完整代码

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 保证 nums1 是较短数组
        if (nums1.length > nums2.length) {
            return findMedianSortedArrays(nums2, nums1);
        }

        int m = nums1.length;
        int n = nums2.length;

        int left = 0, right = m;

        while (left <= right) {
            int i = (left + right) / 2;
            int j = (m + n + 1) / 2 - i;

            int A_left_max  = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1];
            int A_right_min = (i == m) ? Integer.MAX_VALUE : nums1[i];

            int B_left_max  = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];
            int B_right_min = (j == n) ? Integer.MAX_VALUE : nums2[j];

            if (A_left_max <= B_right_min && B_left_max <= A_right_min) {
                if ((m + n) % 2 == 1) {
                    return Math.max(A_left_max, B_left_max);
                } else {
                    return (Math.max(A_left_max, B_left_max)
                          + Math.min(A_right_min, B_right_min)) / 2.0;
                }
            } else if (A_left_max > B_right_min) {
                right = i - 1;
            } else {
                left = i + 1;
            }
        }
        return 0.0;
    }
}

七、二分查找的「判定条件」才是灵魂

A_left_max <= B_right_min
&&
B_left_max <= A_right_min

这句本质是在判断:

左半部分最大值 ≤ 右半部分最小值

也就是:

max(left) ≤ min(right)

一旦成立:

  • 分割完成
  • 中位数就在分割线附近

八、奇偶情况统一处理的技巧

为什么用 (m + n + 1) / 2

  • 奇数时:左边多一个元素
  • 偶数时:左右刚好相等

👉 一行公式统一所有情况,非常优雅


九、时间 & 空间复杂度分析

指标复杂度
时间复杂度O(log(min(m,n)))
空间复杂度O(1)

这是这道题的 理论最优解


十、这道题真正想考什么?

❌ 不是数组
❌ 不是中位数
❌ 不是数学

是你能不能把「二分查找」用在“答案空间”上


十一、总结一句话版

在较短数组上二分,
找到一个分割点,
让左右两边数量平衡且整体有序,
中位数自然浮现。


十二、写在最后

这道题是:

  • 二分查找的 认知分水岭
  • 从「会用模板」到「理解思想」的关键一步

如果你能把这题讲清楚,
那你对 二分查找的掌控力已经非常扎实了 💪