-
先说临界情况
- A为空或者B为空
直接在非空数组中找第k大的数即可。O(1) - 找最小的数,k==0的情况,也简单,比较两个数组最开头的元素,谁小就是谁
-
然后就是比较复杂的情况,假设寻找目标target是下标为k的数。
那么意味着在排好的数组中,在目标数之前,一共有k个比目标更小的数。
将k分成两份,一份在A的前端,一份在B的前端。这里其实将k怎么分配是一个可以讨论的问题,但是平分k可以得到平均最快的效果。
设k = ka + kb,(k是偶数简单,k是奇数的话,剩下那一个随便放在两个数组中哪一个中都可以)这里可以列出来我们想要的效果:
k=1 —-> ka = 1, kb = 1
k=2 —-> ka = 1, kb = 1
k=3 —-> ka = 1, kb = 1. [+1,表示还有一个元素,可以随意分配在ka或者kb中,只要不越界]
k=4 —-> ka = 2, kb = 2
k=5 —-> ka = 2, kb = 2. [+1]
已经可以看出来规律了,这个造成了下面代码中比较复杂的部分,这些细节消耗的时间不少啊。然后就是主要逻辑:
- 如果A[ka-1] >= B[kb-1]
说明在B前端的kb个数中,不可能出现我们要寻找的目标。
为什么呢?
假如A一共有m个数,B一共有n个数。
那么target(下标是k)后面有且只有n + m – 1 – k个数;
但是B[kb-1]在B中已经排在n – kb个数之前,加上A中的m – ka + 1(A[ka-a]),也就是说在排序后的整体数组中排在B[kb-1]之后的数,至少有n – kb + m – ka + 1 = n + m – k + 1个。
由于n + m – k + 1 > n + m – k – 1,所以B前端kb个数都不可能是target。
所以此时可以将问题转化为,在A[0,...,m-1]和B[kb,...,n-1]中,寻找下标为k – kb的数。 - 否则,A[ka-1] < B[kb-1]
同上,可以剔除A前端的ka个数。
这样循环下去,就能以二分的速度找到目标。
这个问题不仅要找到第k大的数,当C是偶数的时候,还要找到第k+1个数,取两者均值。
public class MedianOfTwoSortedArrays {
public double findMedianSortedArrays(int A[], int B[], int m, int n) {
// Start typing your Java solution below
// DO NOT write main() function
int lowA =0;
int lowB = 0;
int highA = m - 1;
int highB = n - 1;
int k = (m + n) / 2;
if((m + n) % 2 == 0){
return (findKth(A, B, lowA, highA, lowB, highB, k) + findKth(A, B, lowA, highA, lowB, highB, k + 1)) / 2.0;
}
else
return findKth(A, B, lowA, highA, lowB, highB, k);
}
public int findKth(int A[], int B[], int lowA, int highA, int lowB, int highB, int k){
int m = highA - lowA + 1;
int n = highB - lowB + 1;
if (m == 0)
return B[lowB + k];
if (n == 0)
return A[lowA + k];
if (k == 0 )
return A[lowA] > B[lowB] ? B[lowB] : A[lowA];
/*
int midA = (lowA + highA) / 2;
int midB = (lowB + highB) / 2;
if(A[midA] > B[midB]){
if((midA + midB + 1) >= k)
return findKth(A, B, lowA, midA, lowB, highB, k);
else
return findKth(A, B, lowA, highA, midB + 1, highB, k - (n / 2 + 1));
}
else{
if((midA + midB + 1) >= k)
return findKth(A, B, lowA, highA, lowB, midB, k);
else
return findKth(A, B, midA + 1, highA, lowB, highB, k - (m / 2 + 1));
}
*/
// Reduce search ranges in A and B
int midA = m * k / (m + n);
int midB = k - midA - 1;
// Add offset so that imid_A and imid_B index directly into A and B, respectively
midA += lowA;
midB += lowB;
if (A[midA] > B[midB]) {
k -= midB - lowB + 1;
highA = midA;
lowB = midB + 1;
}
else {
k -= midA - lowA + 1;
lowA = midA + 1;
highB = midB;
}
return findKth(A, B, lowA, highA, lowB, highB, k);
}
}