4. 寻找两个正序数组的中位数:用分割点代替合并

题目

给定两个大小分别为 mn 的正序数组 nums1nums2,返回两个数组合并后的中位数。

题目要求时间复杂度为 O(log(m+n))

例如:

复制代码
nums1 = [1, 3]
nums2 = [2]

合并后:[1, 2, 3]
中位数:2

初始思路为什么不满足要求

把两个数组拼接起来,再排序,时间复杂度至少是 O((m+n)log(m+n))

即使使用双指针在线性时间内完成合并,也需要 O(m+n),仍然没有达到题目要求的对数复杂度。

因此,关键是不要真正构造合并后的数组,而是直接确定中位数左右两侧的边界。

核心观察:寻找两个分割点

把两个数组分别切成左右两部分:

复制代码
nums1: [左半边] | [右半边]
nums2: [左半边] | [右半边]

i 表示从 nums1 中取多少个元素放入左半边,j 表示从 nums2 中取多少个元素放入左半边。

如果总长度为 m+n,令:

复制代码
half = (m + n + 1) / 2

为了保证左半边元素数量正确,需要满足:

复制代码
i + j = half
j = half - i

这里的 ij 是元素数量,不是最后一个元素的下标。

例如:

复制代码
nums1 = [1, 4, 7, 10]
nums2 = [2, 3, 5, 6, 8, 9]
half = 5

如果 i=2,那么 j=3

复制代码
nums1: [1, 4]    | [7, 10]
nums2: [2, 3, 5] | [6, 8, 9]

左半边一共有 2+3=5 个元素。

有效分割点的条件

两个数组各自有序,所以只需要检查分割点附近的四个边界值:

复制代码
left1  = nums1[i - 1]   // nums1 左半边最大值
right1 = nums1[i]       // nums1 右半边最小值
left2  = nums2[j - 1]   // nums2 左半边最大值
right2 = nums2[j]       // nums2 右半边最小值

有效分割必须满足:

复制代码
left1 <= right2
left2 <= right1

这表示左半边的所有元素都不大于右半边的所有元素。

当分割点位于数组两端时,用无穷边界处理:

复制代码
i == 0:left1  = 负无穷
i == m:right1 = 正无穷
j == 0:left2  = 负无穷
j == n:right2 = 正无穷

为什么只二分较短的数组

先保证 nums1 是较短数组:

复制代码
if (nums1.length > nums2.length) {
    交换 nums1 和 nums2;
}

这样二分范围只有 [0,m],并且根据 j=half-i 推出的 j 一定落在 nums2 的合理范围内,同时可以避免较短数组的分割位置越界。

开区间二分

我们寻找第一个满足下面条件的 i

复制代码
nums1[i] >= nums2[j - 1]

使用开区间哨兵:

复制代码
int left = -1;
int right = m;

其中 left 表示条件不满足的位置,right 表示条件满足的位置或右侧哨兵。每轮计算:

复制代码
int i = left + (right - left) / 2;
int j = half - i;

然后根据 nums1[i]nums2[j-1] 的关系更新边界:

  • 如果 nums1[i] < nums2[j-1],说明 nums1 左半边取少了,i 应该增大:left = i
  • 否则当前 i 可能已经足够,继续向左寻找第一个满足条件的位置:right = i

循环结束时,right 就是最终的 i

手动模拟

仍以:

复制代码
nums1 = [1, 4, 7, 10]
nums2 = [2, 3, 5, 6, 8, 9]
half = 5

为例:

复制代码
left = -1, right = 4
i = 1, j = 4
nums1[i] = 4 < nums2[j-1] = 6
说明 nums1 左边取少了:left = 1

下一轮:

复制代码
left = 1, right = 4
i = 2, j = 3
nums1[i] = 7 >= nums2[j-1] = 5
说明 i 可能已经足够:right = 2

此时 left + 1 == right,二分结束,最终分割为:

复制代码
nums1: [1, 4]    | [7, 10]
nums2: [2, 3, 5] | [6, 8, 9]

左半边最大值为 max(4,5)=5,右半边最小值为 min(7,6)=6,所以中位数为:

复制代码
(5 + 6) / 2 = 5.5

代码实现

复制代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int m = nums1.length;
        int n = nums2.length;
        int half = (m + n + 1) / 2;

        int left = -1;
        int right = m;

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

            if (nums1[i] < nums2[j - 1]) {
                left = i;
            } else {
                right = i;
            }
        }

        int i = right;
        int j = half - i;

        int left1 = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
        int right1 = i == m ? Integer.MAX_VALUE : nums1[i];
        int left2 = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
        int right2 = j == n ? Integer.MAX_VALUE : nums2[j];

        int maxLeft = Math.max(left1, left2);

        if ((m + n) % 2 == 1) {
            return maxLeft;
        }

        int minRight = Math.min(right1, right2);
        return ((double) maxLeft + minRight) / 2.0;
    }
}

复杂度分析

只在较短数组上进行二分,搜索范围为 m,所以时间复杂度为:

复制代码
O(log(min(m,n)))

它满足题目要求的 O(log(m+n))。算法只使用常量级变量,空间复杂度为:

复制代码
O(1)

常见错误

  1. 奇数长度时,中位数应为左半边最大值 max(left1,left2),不能误用右半边的 right1
  2. right1 必须来自 nums1[i]left2 必须来自 nums2[j-1]
  3. 计算偶数长度中位数时,先转为 double,避免两个 int 相加溢出。

总结

这道题的关键不是合并数组,而是寻找一个分割,使得:

复制代码
i + j = half
left1 <= right2
left2 <= right1

只要左半边和右半边的数量、顺序都正确,就可以直接通过四个边界值得到中位数。

相关推荐
Omics Pro7 小时前
AI药物研发10原则!欧盟EMA×美国FDA
数据库·人工智能·算法·机器学习·自然语言处理
wabs6668 小时前
关于二叉树【力扣100.相同的树的思考】
数据结构·c++·算法·leetcode·二叉树·递归法
知知之之8 小时前
敏感数据查询:SHA-256 还是 HMAC-SHA256
算法
彧azz8 小时前
Java学习记录:判断语句
java·笔记·学习·算法
alphaTao8 小时前
LeetCode 每日一题 2026/9/14-2026/9/20
算法·leetcode
hanlin038 小时前
刷题笔记:力扣第560题-和为k的子数组
笔记·算法·leetcode
residual_fan8 小时前
航空发动机故障诊断专用智能体(四):深度强化学习与自适应奖励机制
人工智能·算法·数据挖掘·数据分析
2601_962218618 小时前
万象生鲜系统多终端统一数据协议PC手机PDA数据实时同步
大数据·数据库·人工智能·python·算法
AgentMaster8 小时前
企业元数据管理技术实战:从采集架构到血缘解析的完整方案
大数据·人工智能·算法