LeetCode Ex04 Median of Two Sorted Arrays

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

Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5

该题要求找到两个排序数组的中位数,主要难点是要求时间复杂度为O(log (m+n))。

要解决这个问题,我们需要了解“中位数有什么用”。 在统计中,中位数用于:将一组分成两个相等长度的子集,一个子集总是大于另一个子集。

首先我们切割数组A在一个随机的位置i:

        left_A             |        right_A
  A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]

因为数组A一共有m个元素,所以一共有m+1种切割方法(i=0~m)。
同时,我们可以知道:

       len(left_A)=i,len(right_A)=m−i

我们以同样的方法切割数组B:

         left_B             |        right_B
   B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

我们把left_A和left_B放进一个集合,把right_A和right_B放进一个集合,可以得到:

        left_part          |        right_part
  A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
  B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

如果我们可以保证

    len(left_part)=len(right_part)
    max(left_part)≤min(right_part)

那么我们就已经将数组A和B中的所有元素切割成长度相同的两部分,并且右部总是大于左部。那么我们可以得到:

    median=(max(left_part)+min(right_part))/2

为了确保以上两种情况,我们只需要保证:

   1.i+j=m−i+n−j (or: m-i+n-j+1),
   if n≥m,我们只需设置i=0~m,j=(m+n+1)/2-i
   2.B[j−1]≤A[i] and A[i-1]≤B[j]

简单起见,我们假设A[i-1],B[j-1],A[i],B[j]总是有效的即使i=0,i=m,j=0或者j=n。
为什么要求n≥m?因为我们要确保j是非负的,因为0≤i≤m,j=(m+n+1)/2-i,如果n 所以我们需要做的就是:

   查找i在[0,m],找到一个i能够满足
   B[j−1]≤A[i] and A[i-1]≤B[j],其中j=(m+n+1)/2-i

并且我们可以进行二分查找按照以下步骤:

  1.设置 imin=0,imax=m,开始在[imin,imax]中查找
  2.设置 i=(imin+imax)/2, j=(m+n+1)/2-i
  3.现在我们有len(left_part)=len(right_part),并且接下来我们只会碰到三种情况
  (1)B[j-1]≤A[i]并且A[i-1]≤B[j],意味着我们已经找到了i,停止查找
  (2)B[j-1]>A[i],意味着A[i]太小了,我们必须调整使B[j-1]≤A[i]
     我们可以增加i吗?答案是可以,因为当i增加时,j会减小,那么就可能会出现满足B[j-1]≤A[i]的情况
     所以我们只需增加i,这样我们必须调整查找范围为[i+1,imax],即设置imin=i+1,然后跳转到步骤2
  (3)A[i-1]>B[j],意味着A[i-1]太大了,我们只需减小i,然后调整查找范围为[imin,i-1],设置 imax=i-1,跳转到步骤2

当 i 被找到后,中位数等于:

 max(A[i−1],B[j−1]), 当m+n为奇数
 (max(A[i-1],B[j-1]+max(A[i],B[j]))/2,当m+n为偶数

现在我们来讨论边界问题,即i=0,i=m,j=0或者j=n时,这时A[i-1],B[j-1],A[i],B[j]可能不存在,事实上这种情况的发生比你想象中的简单

我们只需要确保max(left_part)≤min(right_part)。所以当i和j不是边界值时,我们必须检查B[j-1]≤A[i]并且A[i-1]≤B[j]。当某些边界值不存在时,我们不必去检查上述的条件,我们要做的只是:

 查找一个 i 在[0,m]中满足:
 (j=0 or i=m or B[j-1]≤ A[j])并且
 (i=0 or j=n  or A[i-1]≤B[j]) ,其中 j =(m+n+1)/2-i

在这个查找过程中,我们会遇到三种情况:

 (1)(j=0 or i=m or B[j-1]≤ A[j]) and (i=0 or j=n  or A[i-1]≤B[j]),这种情况意味着i 是完美的,停止查找
 (2) j>0 and iA[i],这样 i 太小,我们要增加 i
 (3) i>0 and jB[j],这样 i 太大,我们要减小 i
    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;
    }

Time complexity: O(log(min(m,n))).

你可能感兴趣的:(LeetCode Ex04 Median of Two Sorted Arrays)