【LeetCode 热题100道笔记】寻找两个正序数组的中位数

题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log(m+n))O(log (m+n))O(log(m+n))

示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

提示:

  • nums1.length==mnums1.length == mnums1.length==m
  • nums2.length==nnums2.length == nnums2.length==n
  • 0<=m<=10000 <= m <= 10000<=m<=1000
  • 0<=n<=10000 <= n <= 10000<=n<=1000
  • 1<=m+n<=20001 <= m + n <= 20001<=m+n<=2000
  • −106<=nums1[i],nums2[i]<=106-10^6 <= nums1[i], nums2[i] <= 10^6106<=nums1[i],nums2[i]<=106

思考

采用二分查找法,通过在较短数组上划分左右两部分,找到两个数组的“分割线”,使得分割线左侧的所有元素均小于等于右侧的所有元素。利用中位数的定义(分割线左右元素数量相等或相差1),通过二分调整分割线位置,最终计算出中位数。该方法时间复杂度为O(log(min(m,n))),满足O(log(m+n))的要求。

算法核心原理

  1. 中位数的本质:对于合并后长度为L的数组,中位数是第L//2个元素(奇数)或第L//2-1L//2个元素的平均值(偶数)。
  2. 分割线特性:设分割线在nums1的位置为i(左侧有i个元素),在nums2的位置为j(左侧有j个元素),需满足:
    • i + j = (m + n + 1) // 2(左侧总元素比右侧多1,确保奇数时左侧最大值为中位数)
    • nums1[i-1] <= nums2[j]nums2[j-1] <= nums1[i](左侧所有元素≤右侧所有元素)

算法过程

  1. 确保短数组在前:交换nums1nums2,使m ≤ n(在短数组上二分更高效)。
  2. 二分查找分割线
    • 初始化l=0r=mnums1的分割范围)。
    • 计算i = (l + r) // 2nums1的分割位置),j = halfLen - inums2的分割位置,halfLen = (m + n + 1) // 2)。
    • 定义边界值(处理分割线在数组首尾的情况):
      • maxL1nums1左侧最大值(i=0时为-Infinity
      • minR1nums1右侧最小值(i=m时为Infinity
      • maxL2nums2左侧最大值(j=0时为-Infinity
      • minR2nums2右侧最小值(j=n时为Infinity
    • 调整分割线:
      • maxL1 > minR2:分割线需左移(r = i - 1)。
      • maxL2 > minR1:分割线需右移(l = i + 1)。
      • 否则:找到有效分割线,计算中位数。
  3. 计算中位数
    • 若总长度为奇数:中位数 = max(maxL1, maxL2)
    • 若总长度为偶数:中位数 = (max(maxL1, maxL2) + min(minR1, minR2)) / 2

时空复杂度分析

  • 时间复杂度:O(log(min(m,n))),仅在较短数组上进行二分查找,迭代次数为log(min(m,n))。
  • 空间复杂度:O(1),仅使用常数个变量存储边界值和指针,无额外空间开销。

代码

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
    let [m, n] = [nums1.length, nums2.length];

    if (m > n) {
        [m, n] = [n, m];
        [nums1, nums2] = [nums2, nums1];
    }

    const halfLen = Math.floor((m + n + 1) / 2);
    
    let l = 0, r = m;
    while (l <= r) {
        let i = l + Math.floor((r-l)/2);
        let j = halfLen - i;

        let maxL1 = i === 0 ? -Infinity : nums1[i-1];
        let minR1 = i === m ? Infinity : nums1[i];
        let maxL2 = j === 0 ? -Infinity : nums2[j-1];
        let minR2 = j === n ? Infinity : nums2[j];

        if (maxL1 <= minR2 && maxL2 <= minR1) {
            const len = m + n;
            if (len % 2 === 0) {
                return (Math.max(maxL1, maxL2) + Math.min(minR1, minR2))/2;
            } else {
                return Math.max(maxL1, maxL2);
            }
        } else if (maxL1 > minR2) {
            r = i-1;
        } else if (maxL2 > minR1) {
            l = i + 1;
        }
    }
    
};
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值