LeetCode解题报告—— 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)).

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

分析:关键在于理解中位数的实际含义,即将一群数分割成数目相同的两部分,并且右边的数始终不小于(大于或等于)左边的数,也就是右边的最小值大于等于左边的最大值。明白了这个,在去解这题,对于整数数组 A[0~m] 和 整数数组 B[0~n],在0~m中寻找分割位 i,在 0~n 中寻找分割位 j

First let's cut A into two parts at a random position i:

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

Since A has m elements, so there are m+1 kinds of cutting( i = 0 ~ m ). And we know: len(left_A) = i, len(right_A) = m - i . Note: when i = 0 , left_A is empty, and when i = m , right_A is empty.

With the same way, cut B into two parts at a random position j:

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

Put left_A and left_B into one set, and put right_A and right_B into another set. Let's name them left_part and right_part :

      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]

(上面要注意的是可能构成左,右部分的只有A或B,即A和B被全部扔到了左边部分或右边部分)

If we can ensure:

1) len(left_part) == len(right_part)
2) max(left_part) <= min(right_part)

then we divide all elements in {A, B} into two parts with equal length, and one part is always greater than the other. Then median = (max(left_part) + min(right_part))/2.

To ensure these two conditions, we just need to ensure:

(1) i + j == m - i + n - j (or: m - i + n - j + 1) if n >= m, we just need to set: i = 0 ~ m, j = (m + n + 1)/2 - i (2) B[j-1] <= A[i] and A[i-1] <= B[j]

上面之所以加1是考虑到了 m+n 是奇数的情形,如果m+n是偶数(m=3, n=3),比如是6,那么加1除2和不加1除2结果是一样的都是3,这是如果i=2,那么j=1,left_part是(A[0],A[1],B[0]),right_part是(A[2],B[1],B[2])。如果m+n是奇数(m=3,n=4),7+1除2结果是4,如果i=2,那么j=2,left_part是(A[0],A[1],B[0],B[1]),right_part是(A[2],B[2],B[3])。这时左右部分个数并不相等,因为对于总数为奇数的情形来说本来就不能将其分为个数相等的两个部分,不过从上面的分析可以得知,在总数是偶数时可以保证两部分个数相等,此时的中位数等于左边部分的最大值加上右边部分的最小值再除以2,当总数是奇数时可以保证中位数分到了左边部分,即此时的中位数等于左边部分的最大值。

So, all we need to do is:

Searching i in [0, m], to find an object `i` that: B[j-1] <= A[i] and A[i-1] <= B[j], ( where j = (m + n + 1)/2 - i )

上面要确保m<n,否则索引会出现负数,如果开始输入的两个数组其长度m>n,代码上交换一下数组即可。

And we can do a binary search following steps described below:

<1> Set imin = 0, imax = m, then start searching in [imin, imax]

<2> Set i = (imin + imax)/2, j = (m + n + 1)/2 - i <3> Now we have len(left_part)==len(right_part). And there are only 3 situations that we may encounter: <a> B[j-1] <= A[i] and A[i-1] <= B[j] Means we have found the object `i`, so stop searching. <b> B[j-1] > A[i] Means A[i] is too small. We must `ajust` i to get `B[j-1] <= A[i]`. Can we `increase` i? Yes. Because when i is increased, j will be decreased. So B[j-1] is decreased and A[i] is increased, and `B[j-1] <= A[i]` may be satisfied. Can we `decrease` i? `No!` Because when i is decreased, j will be increased. So B[j-1] is increased and A[i] is decreased, and B[j-1] <= A[i] will be never satisfied. So we must `increase` i. That is, we must ajust the searching range to [i+1, imax]. So, set imin = i+1, and goto <2>. <c> A[i-1] > B[j] Means A[i-1] is too big. And we must `decrease` i to get `A[i-1]<=B[j]`. That is, we must ajust the searching range to [imin, i-1]. So, set imax = i-1, and goto <2>.

When the object i is found, the median is:

max(A[i-1], B[j-1]) (when m + n is odd)
or (max(A[i-1], B[j-1]) + min(A[i], B[j]))/2 (when m + n is even)

Now let's consider the edges values i=0,i=m,j=0,j=n where A[i-1],B[j-1],A[i],B[j] may not exist. Actually this situation is easier than you think.

What we need to do is ensuring that max(left_part) <= min(right_part). So, if i and j are not edges values(means A[i-1],B[j-1],A[i],B[j] all exist), then we must check both B[j-1] <= A[i] and A[i-1] <= B[j]. But if some of A[i-1],B[j-1],A[i],B[j] don't exist, then we don't need to check one(or both) of these two conditions. For example, if i=0, then A[i-1] doesn't exist, then we don't need to check A[i-1] <= B[j]. So, what we need to do is:

Searching i in [0, m], to find an object `i` that:
    (j == 0 or i == m or B[j-1] <= A[i]) and (i == 0 or j == n or A[i-1] <= B[j]) where j = (m + n + 1)/2 - i

And in a searching loop, we will encounter only three situations:

<a> (j == 0 or i == m or B[j-1] <= A[i]) and (i == 0 or j = n or A[i-1] <= B[j]) Means i is perfect, we can stop searching. <b> j > 0 and i < m and B[j - 1] > A[i] Means i is too small, we must increase it. <c> i > 0 and j < n and A[i - 1] > B[j] Means i is too big, we must decrease it.

As: i < m ==> j > 0 and i > 0 ==> j < n . Because: 

m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0 m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n

So in situation <b> and <c>, we don't need to check whether j > 0 and whether j < n.

上面是在考虑边界的情形,在搜索 i 的过程中,因为 j = (m + n + 1)/2 - i,所以我们只需要关注中位数的第二个条件即可,即右边部分的最小值不小于左边部分的最大值,对于分割位i和j,因为数组A和B自身已经是排过序的了,所以首先想到的是比较A[i-1]和B[j],以及B[j-1]和A[i].但是如果i=0,i=m以及j=0,j=m 时该如何判断,以i=0为例,这中情况表明A数组所有数被分到了右边的部分,这种情况大概发生在B数组的个数远大于A数组的情形,此时的中位数在完全的B数组中寻找,j = (m + n + 1)/2 - i,那么j取到的位置就是中间位置,如果此时B[j-1]<=A[i],保证了左边的最大小于或等于右边所有的数,那j的位置就是中位数的位置(或j-1和j)。分析了这么多,其实结论就一个,也就是说当i=0时,只需要满足情形a中与判断的第一条件即可。

代码:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m=nums1.length;
        int n=nums2.length;
        int[] A=nums1;
        int[] B=nums2;
        if(m>n){
            int temp1=m;
            m=n;
            n=temp1;
            int[] temp2=A;
            A=B;
            B=temp2;
        }
        
        int max_left=0;
        int min_right=0;
        // i+j=m-i+n-j+1
        int imin=0,imax=m,half_len=(m+n+1)/2;//出于奇偶的考虑
        while(imin<=imax){
            int i=(imin+imax)/2;
            int j=half_len-i;
            if(i<m && B[j-1]>A[i]) imin=i+1;
            else if(i>0 && A[i-1]>B[j]) imax=i-1;
            else{
       // i is perfect,先求max_left再求max_right
if(i==0){        // A被全部放到了右边 max_left=B[j-1]; }else if(j==0){    // B被全部放到了右边 max_left=A[i-1]; }else{         // 一般情形 max_left=Math.max(A[i-1],B[j-1]); } if((m+n)%2==1) return max_left; if(i==m){ min_right=B[j]; }else if(j==n){ min_right=A[i]; }else{ min_right=Math.min(A[i],B[j]); } return (max_left+min_right)/2.0; } } return -1; } }

猜你喜欢

转载自www.cnblogs.com/f91og/p/9347078.html