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:

  1. get the medians "ma&mb" of array A and B;
  2. 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_|
  3. loop above until subA and subB both have two elements;
  4. 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编辑  收藏  举报

导航