Loading

寻找两个正序数组的中位数

寻找两个正序数组的中位数

方法一:将两个数组合并,然后根据奇偶返回中位数。

  1. 分别判断数组1与数组2是否为空。
  2. 建立新数组,将数组1与数组2根据大小放入。
  3. 根据新数组的奇偶,返回中位数。
完整代码如下
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int m = nums1.length;
    int n = nums2.length;
    if(m == 0){
        if(n % 2 == 0){
            return (nums2[n/2-1] + nums2[n/2])/2;
        }else {
            return nums2[n/2];
        }
    }
    if(n == 0){
        if(m % 2 == 0){
            return (nums1[n/2-1] + nums1[n/2])/2;
        }else{
            return nums1[n/2];
        }
    }
    int count = 0,i = 0,j = 0;
    int nums[] = new int[m + n];
    while(count != i + j){
        if(i == m){
            while(j != n){
                nums[count++] = nums2[j++];
            }
        }
        if(j == n){
            while(i != m){
                nums[count++] = nums1[i++];
            }
        }
        if(nums1[i] < nums2[j]){
            nums[count++] = nums1[i++];
        }else{
            nums[count++] = nums1[j++];
        }
    }
    if(count % 2 == 0){
        return (nums[count / 2 - 1] + nums[count / 2]) / 2.0;
    } else {
        return nums[count / 2];
    }

}
  • 时间复杂度O(m+n),空间复杂度O(m+n)。

方法二:不需要真正合并,只需要遍历找到中位数即可。

  • 用len表示合并后数组长度,若len为奇数,则只需要遍历到len/2个数,若len为偶数,则需要遍历出len/2,与len/2-1这两个数,所以遍历的话,奇数和偶数都是 len/2+1 次。
  • 若为奇数,则只需要保存最后一个,也就是len/2个数就可以,若为偶数,需要保存两个数,最后一个和倒数第二个。所以我们使用两个变量left保存上一次遍历的数值,right保存当前遍历数值。
  • 遍历时按大小遍历,若A数组的值小于B数组且A数组未遍历完,则A数组向后移动一位,反之B数组向后移动一位。
  • 每次都需要判断A,B两数组是否遍历完,所以当A数组未遍历完 && (B数组已至最后一位 || A数组的值小于B数组)时,取A数组的值,反之则取B数组的值。
代码如下
public double findMedianSortedArrays1(int[] nums1, int[] nums2) {
    int m = nums1.length;
    int n = nums2.length;
    int left = -1, right = -1;
    int len = m + n;
    int aStart = 0, bStart = 0;
    for (int i = 0; i <= len / 2; i++) {
        left = right;
        if (aStart < m && (bStart >= n || nums1[aStart] < nums2[bStart])) {
            right = nums1[aStart++];
        } else {
            right = nums2[bStart++];
        }
    }
    if (len % 2 == 0) {
        return (left + right) / 2.0;
    } else {
        return right;
    }
}
  • 时间复杂度为O(m+n),空间复杂度为O(1)。
    注意,必须考虑中位数为小数的情况,所以除数是2.0

方法三:使用二分法

  • 在方法二中我们每次遍历其实是相当于排除一个不可能是中位数的,那么能不能一次排除多个呢,这里就可以使用二分法。

  • 二分法查找的思路如下:
    (1)首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
    (2)如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
    (3)如果某一步数组为空,则表示找不到目标元素。

  • 在本题中,假设数组为

    int[] nums1 = new int[]{1, 2, 3, 4, 5, 8, 9,12};
    int[] nums2 = new int[]{6, 7, 10, 11};
    
  • 我们需要查找的数组总长度为11,那我们就需要找到(11+1)/2第6个数,他就是中位数,那么k=6/2=3,首先比较两个数组的第三个数,由于3 < 10,那么num1数组的1,2,3需要排除掉,此时nums1 = {4, 5, 8, 9,12},nums1 = {6, 7, 10, 11}

  • 本来需要找到第6个数,现在排除了3个,那么我们现在只需要找到第6 - 3 = 3 ,也就是找到剩余两个数组中第三小的数字,这就是我们要找的中位数。

  • 比较剩余数组的第三个数,8 < 10,那么num1数组的4,5,8继续排除掉,此时nums1 = {9,12},nums1 = {6, 7, 10, 11},本来要找第三小的数字,排除三个数后,剩下数组中排第1小的数字就是我们要找的中位数,即中位数 = 6。

  • 注意遍历数组时不能超出数组长度,如果发现k/2大于数组长度,那么这个数组就是为空了,所以我们另设一个程序出口,当有一方数组为空的时候,此时剩下数组的第k个数就是我们要找的中位数。

代码如下
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int m = nums1.length;
    int n = nums2.length;
    int left = (m + n + 1) / 2;
    int right = (m + n + 2) / 2;
    //合并奇数和偶数情况,对于3,无论+1还是+2都是要找第2个数,4/2=2,5/2=2
    return (getKth(nums1, 0, m - 1, nums2, 0, n - 1, left) + getKth(nums1, 0, m - 1, nums2, 0, n - 1, right)) / 2.0;
}

public 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;

    //返回情况3种
    //返回情况1,数组1为空
    if(len1 == 0){
        return nums2[start2 + k - 1];
    }
    //返回情况2,数组2为空
    if(len2 == 0){
        return nums1[start1 + k - 1];
    }
    //返回情况3,k=1,返回第k个数
    if(k == 1){
        return Math.min(nums1[start1],nums2[start2]);
    }

    //指针向前推进k/2,为什么-1?因为数组是从0开始的
    int i = start1 + Math.min(len1, k / 2) - 1;
    int j = start2 + Math.min(len2, k / 2) - 1;

    //K向前推进
    //数组1向前推进
    if(nums1[i] < nums2[j]){
        return getKth(nums1,i + 1,end1, nums2, start2, end2, k - (i - start1 + 1));
    }else{
        return getKth(nums1, start1, end1, nums2, j + 1, end2, k - (j -  start2 + 1));
    }
}
  • 时间复杂度O(log(m+n)),空间复杂度O(1)。
posted @ 2020-08-01 15:59  水纸杯  阅读(1010)  评论(0)    收藏  举报