1. 程式人生 > >LeetCodeLeetCode 兩個排序陣列的中位數問題

LeetCodeLeetCode 兩個排序陣列的中位數問題

有兩個大小為 m 和 n 的排序陣列 nums1 和 nums2 。

請找出兩個排序陣列的中位數並且總的執行時間複雜度為 O(log (m+n)) 。

示例 1:

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

中位數是 2.0
 

示例 2:

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

中位數是 (2 + 3)/2 = 2.5

這是問題,我剛開始刷leetCode還沒多久,剛開始做這個題的時候,覺得真的挺難得。他的難點主要就是時間複雜度的問題。然後做了兩三個小時實在做不出來我就找了看了官方給的解釋,因為英語是個半吊子,理解起來蠻吃力的。查部落格的話,大多隻是寫了個大概,對於我這個渣渣來說理解起來很吃力。所以我就想寫一個更詳細點的!

  做這個題的時候我們要注意題目的意思:題目所給的兩個陣列已經排好序了,如果沒注意到這點,這個題是沒辦法做。然後開始分析:

(1)本題的難點是要有了複雜度,也就是log(n+m) 看見log我們所能想到的肯定是用到了分冶法,不然不會出現這個玩意的。

(2)然後開始理解中位數,就是能把一個數組分成兩半。也就是 左邊的最大的數 小於等於 右邊最小的數

接下來就可以了:暫時不考慮極端情況,假設陣列A和陣列B長度都大於2;

A: A[0] A[1] A[2]..........A[m-3] A[m-2] A[m-1]

B: B[0] B[1] B[2]............B[n-3] B[n-2] B[n-1]


那麼:分析一下, A和B的長度和起來是n+m,一般的長度就是 (n+m)/2

也就是 m/2 +n/2 那我們可以直接把 A和前半部分和B的前半部分 直接拿出來

A: A[0] A[1] A[2]...... A[j-1] | A[j] ....A[m-3] A[m-2] A[m-1] 將A分成了兩半

B: B[0] B[1] B[2]....... B[i-1] | B[i].....B[n-3] B[n-2] B[n-1] 將B分成了兩半

A[j]是A的中位數,B[i]是B的中位數,這個中位數很好求的,(j=m/2 i=n/2)


然後我們將 A的前半部分和B的前半部分 合在一起,把A的後半部分和B的後半部分合在一起, 假如點半部分合起來的最大值 小於等於後半部分的最小值,那麼中位數不是直接就求出了了麼。用一個關係式表示:

   j+i=(n+m)/2    && max(A[j-1] B[i-1]) <=  min(A[j] B[i])  

i和j是有關係的,這裡不再討論,看後面的關係式:要注意 A[j-1] 是一定小於 A[j]的(A和B是有序的)所以我們只需要讓A[j-1] <=B[j] 就行,同理讓 B[i-1] <= A[j] ,這樣做的目的是什麼呢?

  因為實際情況肯定和我們想的不一樣:

情況1:

A[j-1] > B[i] 了怎麼辦? 這時候我們可以理解為,A的左邊有資料過大,所以我們必須把這個大數給 右邊,但是這樣就會產生左邊的總數少了,那我們就把B右邊的給左邊一個就行了:

A: A[0] A[1] A[2]...... A[j-2] | A[j-1] ....A[m-3] A[m-2] A[m-1] j-1 | j+1

B: B[0] B[1] B[2]....... B[i] | B[i+1].....B[n-3] B[n-2] B[n-1] i+1 | i-1

所以左邊合起來的總是依舊等於右邊合起來的總數:

同理:

B[i-1] > A[j] 就把 B[i-1]送給右邊同時把A[i]送給左邊就行。

A: A[0] A[1] A[2]...... A[J] | A[j+1] ....A[m-3] A[m-2] A[m-1] j+1 | j-1

B: B[0] B[1] B[2]....... B[i-2] | B[i-1].....B[n-3] B[n-2] B[n-1] i-1 | i+1

沒有第三種情況:

不可能兩個條件同時不符合:因為 A[j]>= A[j-1] 如果 A[j-1] > B[i] 那麼 A[j] >= A[j-1] > B[i] >=B[i-1] 所以A[j]>B[i-1]

當然如果n活著m是奇數的話,也不需要在意,因為我們要的是 ( max(left_part)+min(right_part) )/2

 補充:我們是用 j來表示i的,所以m要小,為的是不讓i算出了是個負數!!!

程式碼:

package day317;

public class leet_04 {
    public double  findMedianSortedArrays(int[] A,int B[]) {
    	int m=A.length;
    	int n=B.length;
    	if(m>n) {//保證m<n 意思就是B的長度大於等於A的長度
    		int[] temp=A;A=B; B=temp;
    		int tmp=m;m=n;n=tmp;
    	}
    	int iMin=0,iMax=m,halfLen=(n+m+1)/2;
    	while(iMin<=iMax) {
    		int i=(iMin+iMax)/2;
    		int j=halfLen-i;
    		if(i<iMax&&B[j-1]>A[i]) {
    			iMin=iMin+1;
    		}
    		else if(i>iMin&&A[i-1]>B[j]) {
    			iMax=iMax-1;
    		}
    		else {
    			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;
    }
    public static void main(String[] args) {
    	int a[]={1,2,3};
    	int b[]= {2,3,4,5,6};
    	leet_04 l=new leet_04();
    	double c=l.findMedianSortedArrays(a,b);
    	System.out.println(c);
    }
}