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);
}
}