这题真是浪费了很多时间,想法也对了,转移到寻找第k小的元素上来,然后根据两个都是一排好序的数组,像类似算法导论中利用partition找到划分的q,但在这里,由于题目要求O(logn),所以这个找q的过程必须是常数时间,想了好久想不到一个常数时间的划分。最后在网上找到答案了:
分析:我们现在设计一个求第k小元素的算法,为了分析方便我这里假使数组的下标是从1开始的。现在先假设数组A,B的元素个数都大于k/2个(为了好分析先这样假设,具体会有不同),现在我们找出A中的第k/2个元素,B中的第k-k/2个元素,比较A[k/2]和B[k-k/2]。
假如:A[k/2]<B[k-k/2],那么其实我们可以把比A[k/2]小及A[k/2]都去掉,因为A[k/2]肯定比第k小的元素小。下面我么来说明一下这一点:假如A[k/2]是第k小,或比第k小大,B[k-k/2]肯定是第k-k/2小的在B中,所以B中比A[k/2]小的最多就k-k/2-1个,A中比A[k/2]小的最多就k/2-1个,那么A,B加起来比A[k/2]小的最多就k/2-1+k-k/2-1=k-2个,也就是说A最大只可能是第k-1小,所以我们可以抛弃A[k/2]及其左边的A中的元素。那么问题就转化为求A[k/2+1,……,m]和B[1,……,n]中第k-k/2小的元素了。
假如:A[k/2]>B[k-k/2],问题就转化为求A[1,……,m]和B[k-k/2+1,……n]中第k-(k-k/2)小的问题了。
假如:A[k/2]=B[k-k/2],那么在A中有k/2-1个比A[k/2]小的在B中也有k-k/2-1个比A[k/2]小的,所以A[k/2]是第k-1小的元素,但B[k-k/2]和A[k/2]一样大,所以A[k/2]也可以说是第k小的元素。
那么我们现在来讨论一下边界情况:假如k/2比m大,我们就选取A[m]和B[k-m]进行比较,依然可以比较出来,这时如果A[m]比B[k-m]小那么就返回B[k-m]就行了,它就是第k小元素。其他情况类似上边的讨论。
代码:
1 #include<iostream> 2 #include<algorithm> 3 4 using namespace std; 5 6 double FindKth(int a[], int m, int b[], int n, int k) 7 { 8 if (m < n) 9 { 10 return FindKth(b, n, a, m, k); 11 } 12 if (m == 0) 13 return b[k-1]; 14 if (k == 1) 15 return min(a[0], b[0]); 16 int pa = min(k / 2,m); 17 int pb = k - k / 2; 18 if (a[pa - 1] < b[pb - 1]) 19 return FindKth(a + pa, m - pa, b, n, k - pa); 20 else if (a[pa - 1]>b[pb - 1]) 21 return FindKth(a, m, b + pb, n - pb, k - pb); 22 else 23 return a[pa - 1]; 24 } 25 26 double MedianOfTwoSortedArrays(int a[], int m, int b[], int n) 27 { 28 int total = m + n; 29 if (total % 2 == 1) 30 return FindKth(a, m, b, n, total / 2 + 1); 31 else 32 return (FindKth(a, m, b, n, total / 2) + FindKth(a, m, b, n, total / 2 + 1))/2; 33 } 34 35 int main() 36 { 37 int a[] = { 1, 2, 4,5}; 38 int b[] = { 3, 5, 6, 7 }; 39 cout << MedianOfTwoSortedArrays(a, 4, b, 4) << endl; 40 }