LeetCode - Median of Two Sorted Arrays
Pre-definition: "median of a array" is the right-middle-value element.
if O(m+n), then Merge Sort is OK!
Note: The overall run time complexity should be O(log (m+n)).
Reaction: Binary-Find!
Input: A[0...m-1] and B[0...n-1]
Steps:
- get the medians "ma&mb" of array A and B;
- compare ma and mb
- if ma == mb, then return ma/mb;
- if ma > mb, then the median lies in the subA[0...|_m/2_|] and subB[|_n/2_|...n-1]
- if ma < mb, then the median lies in the subA[|_m/2_|...m-1] and subB[0...|_n/2_|
- loop above until subA and subB both have two elements;
- then the median = (max(subA[0], subB[0]) + min(subA[1], subB[1])) / 2.
Assume: the number of elements in A and B are both larger than k/2,
Then compare the k/2-th smallest element in A(i.e. A[k/2-1]) and the k/2-th smallest element in B(i.e. B[k/2 - 1])
there are three results: (Becasue k can be odd or even number, so we assume k is even number here for simplicy. The following is also true when k is an odd number.)
- if A[k/2-1] = B[k/2-1], we should return one of them
- A[k/2-1] > B[k/2-1]
- if A[k/2-1] < B[k/2-1], that means all the elements from A[0] to A[k/2-1](i.e. the k/2 smallest elements in A) are in the range of k smallest elements in the union of A and B. Or, in the other word, A[k/2 - 1] can never be larger than the k-th smalleset element in the union of A and B.
Why? We can use a proof by contradiction.
Since A[k/2 - 1] is larger than the k-th smallest element in the union of A and B, then we assume it is the (k+1)-th smallest one. Since it is smaller than B[k/2 - 1], then B[k/2 - 1] should be at least the (k+2)-th smallest one. So there are at most (k/2-1) elements smaller than A[k/2-1] in A, and at most (k/2 - 1) elements smaller than A[k/2-1] in B.So the total number is k/2+k/2-2, which, no matter when k is odd or even, is surly smaller than k(since A[k/2-1] is the (k+1)-th smallest element). So A[k/2-1] can never larger than the k-th smallest element in the union of A and B if A[k/2-1]<B[k/2-1];
Since there is such an important conclusion, we can safely drop the first k/2 element in A, which are definitaly smaller than k-th element in the union of A and B. This is also true for the A[k/2-1] > B[k/2-1] condition, which we should drop the elements in B.
When A[k/2-1] = B[k/2-1], then we have found the k-th smallest element, that is the equal element, we can call it m. There are each (k/2-1) numbers smaller than m in A and B, so m must be the k-th smallest number. So we can call a function recursively, when A[k/2-1] < B[k/2-1], we drop the elements in A, else we drop the elements in B.
We should also consider the edge case, that is, when should we stop?
1. When A or B is empty, we return B[k-1]( or A[k-1]), respectively;
2. When k is 1(when A and B are both not empty), we return the smaller one of A[0] and B[0].
Attached code:
1 double findKth(int a[], int m, int b[], int n, int k) 2 { 3 //always assume that m is equal or smaller than n 4 if (m > n) 5 return findKth(b, n, a, m, k); 6 if (m == 0) 7 return b[k - 1]; 8 if (k == 1) 9 return min(a[0], b[0]); 10 //divide k into two parts 11 int pa = min(k / 2, m), pb = k - pa; 12 if (a[pa - 1] < b[pb - 1]) 13 return findKth(a + pa, m - pa, b, n, k - pa); 14 else if (a[pa - 1] > b[pb - 1]) 15 return findKth(a, m, b + pb, n - pb, k - pb); 16 else 17 return a[pa - 1]; 18 } 19 20 class Solution 21 { 22 public: 23 double findMedianSortedArrays(int A[], int m, int B[], int n) 24 { 25 int total = m + n; 26 if (total & 0x1) 27 return findKth(A, m, B, n, total / 2 + 1); 28 else 29 return (findKth(A, m, B, n, total / 2) 30 + findKth(A, m, B, n, total / 2 + 1)) / 2; 31 } 32 };
References:
http://blog.csdn.net/realxie/article/details/8078043
http://blog.csdn.net/zxzxy1988/article/details/8587244
http://www.cnblogs.com/luxiaoxun/archive/2012/09/13/2684054.html
http://blog.sina.com.cn/s/blog_89b8586f01019x1t.html
A more general version:
Find the Kth element of two sorted arrays A[] and B[].
#include <iostream> #include <string.h> #include <stdlib.h> using namespace std; //Notice : K > 0 int FindKthElm(int A[], int aBeg, int aEnd, int B[], int bBeg, int bEnd, int k) { if (aBeg > aEnd) { return B[bBeg + k - 1]; } if (bBeg > bEnd) { return A[aBeg + k - 1]; } //取中间位置 int aMid = aBeg + (aEnd - aBeg)/2; int bMid = bBeg + (bEnd - bBeg)/2; //从A和B的开始位置到两个数组中间位置的元素个数 int halfLen = aMid - aBeg + bMid - bBeg + 2; if (A[aMid] < B[bMid]) { if (halfLen > k) { // 此时在合并的数组中A[aBeg...aMid]和元素一定在B[bMid]的左侧, // 即此时第k大的元素一定比B[bMid]这个元素小(严格来说不大于) // 故以后没有必要搜索 B[bMid...bEnd]这些元素 return FindKthElm(A, aBeg, aEnd, B, bBeg, bMid - 1, k); } else { // 此时在合并的数组中A[aBeg...aMid]元素一定在B[bMid]的左侧, // 所以前K个元素中一定包含A[aBeg...aMid](可以使用反证法来证明这点) // 但是无法判断A[amid+1...aEnd]与B[bBeg...bEnd]之间的关系,对他们进行判断 // 此时K就剩下除去A[aBeg...aMid]这些元素,个数为k - (aMid - aBeg + 1) return FindKthElm(A, aMid + 1, aEnd, B, bBeg, bEnd, k - (aMid - aBeg + 1)); } } else { //注释与上面相似 if (halfLen > k) { return FindKthElm(A, aBeg, aMid - 1, B, bBeg, bEnd, k); } else { return FindKthElm(A, aBeg, aEnd, B, bMid + 1, bEnd, k - (bMid - bBeg + 1)); } } } int main() { const int ALen = 11; const int BLen = 5; int apos = 0; int bpos = 0; int A[ALen]; int B[ALen]; //生成两个递增数组A 和 B for (int i = 1; i <= ALen + BLen; ++i) { if (apos >= ALen) { B[bpos++] = i; } else if (bpos >= BLen) { A[apos++] = i; } else { if (rand()%2 == 1) { A[apos++] = i; } else { B[bpos++] = i; } } } //输出A和B的内容 for (int i = 0; i < ALen; ++i) { cout <<A[i] <<" "; } cout <<endl; for (int i = 0; i < BLen; ++i) { cout <<B[i] <<" "; } cout <<endl; //验证每个K是不是正解 for (int i = 1; i <= ALen + BLen; ++i) { cout << i <<" : "<<FindKthElm(A, 0 , ALen - 1, B, 0 , BLen - 1, i)<<endl; } return 0; }
posted on 2013-05-03 09:50 highstar88 阅读(239) 评论(0) 编辑 收藏 举报