題目
給定兩個大小爲 m 和 n 的有序數組 nums1
和 nums2
。
請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。
你可以假設 nums1
和 nums2
不會同時爲空。
解析
確定中位數的原理
1.由於時間複雜度要求肯定要用二分法
2.中位數代表着比其小的數的數量,等於比其大的數的數量,那麼可以確定給兩個數組下標分別使兩下標左邊的數的數量等於右邊,則兩下標i,j的和i+j=(m+n+1)/2
3.B[j−1]<=A[i] 且A[j−1]<=B[i]時中值就在這四個數內
尋找中位數的方法 ——二分查找
1.把i設爲m/2(j=(m+n+1)/2-i,)開始比較,如果B[j−1]>=A[i],那麼A[i]太小,要用類似折半查找的方式,在A數組更大的一半中繼續找(選中間的比較),A[j−1]>=B[i]則A[i]太大在A數組更小的一半中繼續找
2.當B[j−1]<=A[i] 且A[j−1]<=B[i]成立時就要考慮各種特殊情況。比如m+n是奇數還是偶數,i,j有沒有到了數列兩邊極限
我是使用迭代的方法,是在答案基礎上改的,但其實更慢,不過時間複雜度也是爲 O(log(m + n))
package leetcode4.尋找兩個有序數組的中位數;
import java.util.Arrays;
public class middle
{
public static void main(String[] args)
{
int[] a=youXuShuZu(0,1000000000,30000000);
int[] b=youXuShuZu(800000000,1800000000,40000000);
//以下是用來比較兩方法的速度,結果是我的慢一點
{
long t1,t2;//引入時間
t1 = System.currentTimeMillis();//計時器
double mid1=0;
for(int i=0;i<100000;i++)
{
mid1=findMedianSortedArrays1(a, b) ;
}
t2 = System.currentTimeMillis();
System.out.println("二分迭代的方法"+(t2-t1)+"ms");//輸出用時
System.out.println("中位數"+mid1);
}
{
long t1,t2;//引入時間
t1 = System.currentTimeMillis();//計時器
double mid2=0;
for(int j=0;j<100000;j++)
{
mid2=findMedianSortedArrays2(a, b) ;
}
t2 = System.currentTimeMillis();
System.out.println("leetcode的方法"+(t2-t1)+"ms");//輸出用時
System.out.println("中位數"+mid2);
}
}
//生成有序數組,確定開頭,結尾,和數組數字量
public static int[] youXuShuZu(int start,int end,int number)
{
int[] a=new int[number];
for(int i=0;i<a.length;i++)
{
a[i]=(int)(Math.random()*(end-start)+start);
}
Arrays.sort(a);
//System.out.println("有序數組"+Arrays.toString(a));
return a;
}
//我的是使用迭代的方法
public static double findMedianSortedArrays1(int[] A, int[] B)
{
int m = A.length;
int n = B.length;
double mid;
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;
mid=dieDai(A,B,iMin,iMax,halfLen);
return mid;
}
//leetcode那個標準答案只是第一步二分了,後面沒有實際上時間複雜度不是O(log(m+n)),要像下面這種迭代了的纔是
public static double dieDai(int[] A,int[] B,int iMin,int iMax,int halfLen)
{
int i = (iMin + iMax) / 2;//兩者之和爲奇數,則A[i]爲A數組中數,兩者之和爲偶數,則A[i-1]和A[i]共同爲中數
int j = halfLen - i;
//跳出這個循環的第一種情況B[j-1] <= A[i]
if (i < iMax && B[j-1] > A[i])
{
// i is too small
iMin=i+1;//爲什麼是iMax=i呢,因爲A[i-1] > B[j]的話,最後的中位數肯定不是A[i],所以類比於從0開始,現在要從i+1開始
return dieDai(A,B,iMin,iMax,halfLen);
}
//跳出這個循環的第一種情況A[i]>=B[j-1],A[i-1] <=B[j],如果是這種情況i起碼是以iMin+1跳出,如果第二種情況則由於i=iMin跳出,即A[i]>B[j]是符合的
else if (i > iMin && A[i-1] > B[j])
{
// i is too big
iMax=i; //爲什麼是iMax=i呢,因爲A[i-1] > B[j]的話,最後的中位數肯定不是A[i],類比以前以a.length+1(即m),現在要從i-1+1開始
return dieDai(A,B,iMin,iMax,halfLen);
}
else
{ // i is perfect
//這裏分析的時候一定要用答案解析中的分兩塊的思路,即答案肯定在A[i],A[i-1],B[j],B[j-1],之內,不要來回分析數組長度先奇後偶,或反過來有什麼區別,這樣來回分析情況太多
//是不是中數,看比這個數小的數有多少就可以了
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }//就是i已經是a數組最左,且i==0即i==iMin跳出說明A[0]>B[j]也>B[j-1],那麼,那麼b數組取j-1,因爲m,n都是數組長度,是數組最右端下標加1,所以如果一奇一偶的話j-1是中值,如果是兩奇或兩偶j-1則是左中值,因爲A[0]>B[j-1]
else if (j == 0) { maxLeft = A[i-1]; }//這種情況是n=m,則這種情況下i=iMax=n,同上道理i-1是中值或左中值
else { maxLeft = Math.max(A[i-1], B[j-1]); }//m+n爲偶數時,必爲左中數,m+n爲單數時,加入A[i-1]>B[j-1],那麼比A[i-1]小的數有,(i-1)+(j-1+1)正好是m+n-1的一半,也就是說A[i-1]是中位數,對於B[j-1]>A[i-1]的情況同理
if ( (A.length + B.length) % 2 == 1 ) { return maxLeft; }//根據上訴分析如果m+n爲單數,那麼左中值就是中值
int minRight = 0;
if (i == A.length) { minRight = B[j]; }//同上分析
else if (j == B.length) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
//leetcode的方法
public static double findMedianSortedArrays2(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;
}
}