有序数组的中位数

4. Median of Two Sorted Arrays

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

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

The median is 2.0
中位数定义法

求中位数,即求位置在中间的数。这是一种很好理解并实现的方法。根据定义,我们可以使用指针指向最小的数字开始,之后指向到比这个数字大的最小的数字。经过两组数组的长度的一半。即我们要找的中位数。时间复杂度为O((m+n)/2)。空间复杂度为O(1)。

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    
    int halfLength = (nums1.length + nums2.length) / 2;
    int i = 0,j = 0,cur = 0;

    int[] start = new int[halfLength + 1];
    while(cur <= halfLength){
        if(i == nums1.length && j == nums2.length){
            break;
        } else if(i == nums1.length){
            start[cur] = nums2[j++];
        } else if(j == nums2.length){
            start[cur] = nums1[i++];
        } else if (nums1[i] < nums2[j]){
            start[cur] = nums1[i++];
        } else{
            start[cur] = nums2[j++];
        } 
        cur++;
    }
    
    if((nums1.length + nums2.length) % 2 == 1){
        return (double)start[cur - 1];
    } else {
        return (start[cur - 2] + start[cur - 1])/2.0;
    }
}
统计方法

按照中位数的定义,如果将一组有序数字分为两半,若这组数字的数字个数为偶数,则中位数为前一半最大的数和后一半最小的数字和的一半。若为数字个数为奇数,则中位数为前一半最大的数。

对于两组数字。我们可以将其合并为一组数字,然后根据上述的方式进行寻找。

如数字

1 2 3 4 5 
    3 4 5  

我们很容易会发现,这个中位数就是 (3+4)/2 = 3.5。

我们中间可以画一条拆分线,拆分线的左边就是两半中小的数字,右边就是两年中大的数字。

1 2 3 | 4 5 
    3 | 4 5  

对于奇数,同理也如此。

1 2 3 | 4
    3 | 4 5

因此我们的任务就是找到 这一条拆分线。

设置两个指针i 和 j。j 随着 i 动而动。i和j的关系就是 j = (m+n+1)/2 - i,其中m,n为两个数组的长度。我们可以将其短的数组放在前面,将长的数组放在后面整合成新的数组。这么做可以避免j出现溢出的情况。

调整i,指导找出符合条件的拆分线的条件的i。

我们要找的拆分线处满足的条件就是 A[i] ≥ B[j - 1],A[i - 1] ≤ B[j]。

3 4 5 1 2 3 4 5
public double findMedianSortedArrays(int[] A, int[] B) {
    int m = A.length;
    int n = B.length;
    if (m > n) { // to ensure m<=n
        int[] temp = A; A = B; B = temp;
        int tmp = m; m = n; n = tmp;
    }
    int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
    while (iMin <= iMax) {
        int i = (iMin + iMax) / 2;
        int j = halfLen - i;
        if (i < iMax && B[j-1] > A[i]){
            iMin = i + 1; // i is too small
        }
        else if (i > iMin && A[i-1] > B[j]) {
            iMax = i - 1; // i is too big
        }
        else { // i is perfect
            int maxLeft = 0;
            if (i == 0) { maxLeft = B[j-1]; }
            else if (j == 0) { maxLeft = A[i-1]; }
            else { maxLeft = Math.max(A[i-1], B[j-1]); }
            if ( (m + n) % 2 == 1 ) { return maxLeft; }

            int minRight = 0;
            if (i == m) { minRight = B[j]; }
            else if (j == n) { minRight = A[i]; }
            else { minRight = Math.min(B[j], A[i]); }

            return (maxLeft + minRight) / 2.0;
        }
    }
    return 0.0;
}
二分(转换成寻找第k小的问题)
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int total = nums1.length + nums2.length;
    return (getKmin(nums1,0,nums2,0,(total+ 1)/2) + getKmin(nums1,0,nums2,0,(total + 2)/2))/2.0;
}
public double getKmin(int[] nums1,int m, int[] nums2,int n,int k){
    if(m > nums1.length - 1){
        return nums2[n + k -1];
    }
    if(n > nums2.length -1){
        return nums1[m + k - 1];  
    }
    if(k == 1){
        return Math.min(nums1[m],nums2[n]);
    }
    
    int min1 = Integer.MAX_VALUE;
    int min2 = Integer.MAX_VALUE;
    if(m + k/2 -1 < nums1.length){
        min1 = nums1[m + k/2 -1];
    }
    if(n + k/2 -1 < nums2.length){
        min2 = nums2[n + k/2 -1];
    }
    
    return min1 < min2 ? getKmin(nums1,m + k/2,nums2,n,k-k/2):getKmin(nums1,m,nums2,n+k/2,k-k/2);
}

文章作者: 彭峰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 彭峰 !
 上一篇
2021-05-02 彭峰
下一篇 
2021-05-02 彭峰
  目录