1. 程式人生 > >Leetcode4 Median of Two Sorted Arrays

Leetcode4 Median of Two Sorted Arrays

這是一道Hard的題,確實是難誒,感覺就是道數學題,像高考數學求導的那道大題一樣,各種情況和邊界條件需要考慮,還需要推導。自己沒寫出來,

主要參考Solution: https://leetcode.com/problems/median-of-two-sorted-arrays 和discuss:https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2481/Share-my-O(log(min(mn))-solution-with-explanation

他們的思路是一樣的,不過一個是java一個是python而已。

詳細的可以去看原文,這裡只整理思路。

首先是根據中位數的定義,將問題等價為了

      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]

用i,j去分割AB兩個陣列,如果上面的分割可以得到中位數,那麼有

1) len(left_part) == len(right_part) or len(right_part)+1 (人為規定如果odd的話leftpart大一點,這個無所謂,leftpart大的話就從leftpart找結果就行)
2) max(left_part) <= min(right_part)

那麼even: median = (max(left_part) + min(right_part))/2 ;odd: median=left_part[-1]

上面兩條件等價:

 1) i+j = m-i+n-j or m-i+n-j+1

 2) A[i-1]<=B[j] and B[j-1]<=A[i]

其中對於1)我們可以約束m<=n,那麼有 j = (m+n+1)//2-i 。於是:

 1)  j = (m+n+1)//2-i   s.t.m<=n

 2) A[i-1]<=B[j] and B[j-1]<=A[i]

在條件1)下,有了i我們就有了j,於是我們在[0,m]裡對i進行二分查詢。(為什麼是左閉右閉呢,因為i其實是一個位置,而不是一個元素,比如i=0,代表在元素0之前分割,i=m,代表在元素m之前(元素m不存在,等價與元素m-1之後分割))

對於2)i=0時,A[i-1]不存在;類似的,j=0,i=m,j=n時,B[j-1],A[i],B[j]不存在,所以這個邊界條件我們也要處理一下。

i=0時,說明我們對i的搜尋已經進行完畢,A組資料都應該在rightpart,那麼i就是我們要的i了,至於結果怎麼樣要看是odd還是even了;

i=m時,A組資料都應該在leftpart,那麼i就是我們要的i了;

j類似。

所以,i什麼時候小呢?

B[j-1]>A[i]&&j!=0&&i!=m

等價於 B[j-1]>A[i]&&j>0&&i<m

i什麼時候大呢?

A[i-1]>B[j]&&i!=0&&j!=n

等價於 A[i-1]>B[j]&&i>0&&j<n

剩下的時候就是i合適了。

但是這時i=0,j=0,i=m,j=n這幾個特殊情況會影響我們的公式,所以要特殊處理。

照這個思路寫了一版,並不對,先放這兒吧。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m=nums1.length,n=nums2.length;
        //ensure m<=n orelse j=(m+n+1)/2-i might be a negative interger ,that's not what we want
        if(m>n) {
            int[] temp=nums1;nums1=nums2;nums2=temp;
            int tempi = m;m=n;n=tempi;
        }
        if(m==0) {
            if(n%2==0) return (nums2[n/2-1]+nums2[n/2])/2.0;
            else return (double)nums2[n/2];
        }
        //i,j represents where we divide nums1,nums2 to two equivalent parts or len(left)==len(right)+1
        int i=0,j=(m+n+1)/2-i; 
        //binary search
        int pl=0,pr=m;
        while(pl<=pr) {
            i=(pl+pr)/2;
            j=(m+n+1)/2-i;
            int leftmax,rightmin;
            if(j>0&&i<m&&nums2[j-1]>nums1[i]) { //i is too small
                pl=(pl+pr)/2+1;
            }
            else if(i>0&&j<n&&nums1[i-1]>nums2[j]) {  //i is too big
                pr=(pl+pr)/2-1;
            }
            else { //i,j are what we want
                if(i==0) leftmax=nums2[j-1];
                else if(j==0) leftmax=nums1[i-1];
                else leftmax=Math.max(nums1[i-1],nums2[j-1]);
                if(i==m) rightmin=nums2[j];
                else if(j==n) rightmin=nums1[i];
                else rightmin = Math.max(nums1[i], nums2[j]);
                if((m+n)%2==0) return (leftmax+rightmin)/2.0;
                else return (double)leftmax;
            }
        }
        return 0.0;
    }
}
Wrong Answer Input [1,2]
[-1,3] Output 2.0 Expected 1.5