4.LeetCode-寻找两个正序数组的中位数

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

算法的时间复杂度应该为 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 == m

  • nums2.length == n

  • 0 <= m <= 1000

  • 0 <= n <= 1000

  • 1 <= m + n <= 2000

  • -10^6 <= nums1[i], nums2[i] <= 10^6

思路1:

老暴力拉。合并数组,排序好再取中位数。(空间复杂度O(m+n),时间复杂度O(nlogn),不排序,可优化为遍历数组的时候排好顺序 空间复杂度也得O(m+n))

 public static double findMedianSortedArrays1(int[] nums1, int[] nums2) {
    int[] nums = Arrays.copyOf(nums1, nums1.length + nums2.length);
    System.arraycopy(nums2, 0, nums, nums1.length, nums2.length);
    Arrays.sort(nums);

    int length = nums.length;
    if (length%2 == 0){
        System.out.println(nums[length/2]);
        System.out.println(nums[length/2 -1]);
        return (nums[length/2] + nums[length/2 -1])/2.0;
    }
    return nums[length/2];
}

思路2:

已经排好序的数组以及时间复杂度log,明显提示使用二分法。

两个有序数组,要求找到两个有序数组的中位数,最直观的思路有以下两种:

  1. 使用归并的方式,合并两个有序数组,得到一个大的有序数组。大的有序数组的中间位置的元素,即为中位数。

  1. 不需要合并两个有序数组,只要找到中位数的位置即可。由于两个数组的长度已知,因此中位数对应的两个数组的下标之和也是已知的。维护两个指针,初始时分别指向两个数组的下标 0 的位置,每次将指向较小值的指针后移一位(如果一个指针已经到达数组末尾,则只需要移动另一个数组的指针),直到到达中位数的位置。

第一思路的时间复杂度时O(m+n),空间复杂度时O(m+n).

第一思路的时间复杂度时O(m+n),空间复杂度时O(1).

public static double findMedianSortedArrays2(int[] nums1, int[] nums2) {
    int m = nums1.length, n = nums2.length;
    int len = m+n;
    int left = -1,  right = -1;//初始化中位两个元素为-1
    int startA = 0, startB = 0;//两个数组的指针
    for (int i = 0; i <=len/2 ; i++) {//至多len/2+1次偏移
        left = right;
        if (startA<m && (startB>=n || nums1[startA] < nums2[startB])){
            right = nums1[startA++];//指针A小于数组1的总长度且数组1对应的元素小于数组2的时候 偏移指针A(限制条件:指针B未超长)
        }else {
            right = nums2[startB++];
        }
    }

    if ((len&1) ==0){//位运算 m+n为偶数时
        return (left + right) / 2.0;
    }else {
        return right;
    }
}

思路3:

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

根据中位数的定义,当 m+n是奇数时,中位数是两个有序数组中的第 (m+n)/2 个元素,当 m+n 是偶数时,中位数是两个有序数组中的第 (m+n)/2(个元素和第 (m+n)/2+1 个元素的平均值。因此,这道题可以转化成寻找两个有序数组中的第 k 小的数,其中 k 为 (m+n)/2或 (m+n)/2+1.因为数组是排好序的,我们可以通过二分查找,每次筛去k/2个。

无论是找第奇数个还是第偶数个数字,对我们的算法并没有影响,而且在算法进行中,k 的值都有可能从奇数变为偶数,最终都会变为 1 或者由于一个数组空了,直接返回结果。

所以我们采用递归的思路,为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) 对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k=1 或者其中一个数字长度是 0 了.

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int n = nums1.length;
    int m = nums2.length;
    int left = (n + m + 1) / 2;
    int right = (n + m + 2) / 2;
    //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
    return (getKth(nums1, 0, n - 1, nums2, 0, m - 1, left) + getKth(nums1, 0, n - 1, nums2, 0, m - 1, right)) * 0.5;  
}
    
    private int getKth(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1 
        if (len1 > len2) return getKth(nums2, start2, end2, nums1, start1, end1, k);
        if (len1 == 0) return nums2[start2 + k - 1];

        if (k == 1) return Math.min(nums1[start1], nums2[start2]);

        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if (nums1[i] > nums2[j]) {
            return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        }
        else {
            return getKth(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值