[LeetCode]Median of Two Sorted Arrays

题目要求:

There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

 

如果使用像归并排序那样的归并方式,从头遍历A和B,比较A和B中的最前的数,取出最小数直到第K个(K为中位数所在个数),

这种方法的时间复杂度为O(m+n),而题目要求时间复杂度为O(log(m+n)),因此需要想一个别的方法。

看到O(log(m+n)),我想到使用qsort找第K个最小数的方法,通过每次比较A和B的中间值来决定K在A或B哪个数组中的前或后半部分。

但后来发现边界条件太多,写出来的代码只适用于普通的情况。直到我搜索到一个网页,上面翻译了论坛上别人写的算法,我觉得很不错,

实现后也的确AC了。原网页地址是http://www.2cto.com/kf/201309/242494.html

 

下面来学习一下这种不错的解法。

找出两个数组中的中位数,首先确定一下数组的元素个数,如果是奇数,则中间位置的数即为中位数,若为偶数则中间2个位置的数之和除以2,

即为中位数。

现在我们可以把求中位数转化为求数组中的第K个小的数,K=(n+m+1) / 2。则数组元素个数为奇数,就求出第K个,若为偶数,就求出第K个

和第K+1个数,它们的和除以2就是所求了。

如何找出这两个数组中的第K个小的数?我们先假设2个数组中最短的数组为A,A的元素个数为m,B的元素个数为n。

因为K个数必来自A或B中,所以我们假设把k个数分成两部分,一部分在A中,一部分在B中,就有如下式子:

 int tmp = k / 2;
 int pa = tmp < m ? tmp : m;
 int pb = k - pa;

 这个时候,在我们的假设中,k个小的数中,有pa个在A中,pb个在B中。

 接下来比较一下A[pa - 1]和B[pb - 1]的大小,会有3种情况:

 1.  A[pa - 1] < B[pb - 1],则说明在k个小的数中,必有pa个数在A中。利用反证法证明。

      假设没有pa个数在这K个数中,则A[pa -1]必是这K个数之外的,我们可以设A[pa -1]为第k+1个数,

      那么B[pb - 1]必为第k+1之后的数,设为第k+2个。因为A[pa - 1]之前A中只能有pa - 1 个数,而B[pb -1]

      之前只能有pb - 1个数,A[pa -1]已经是第k+1个,也就是说只能有pa-1+pb-1 = k-2个数在k个数里,形

      成矛盾。所以必有pa个数在A中得证。那么我们需要找其他的k-pa个数,这些数在A[pa-1]之后和B的整个

      数组中。

2. A[pa - 1] > B[pb - 1],说明在k个小的数中,必有pb个数在B中。证明同情况1。剩下的k-pb个数就在

       B[pb-1]之后和A的整个数组中。

3. A[pa - 1] = B[pb - 1],说明刚好这k个数都在0到A[pa-1]和0到B[pb-1]中。

    

有了以上分析,可以写出代码,但还需要注意一些边界条件:

     a. 为了使用上面的分析,首先要保证一个数组默认为最短的,如果不合条件要反向输入参数调用递归函数,如代码中所示;

     b. 如果短的数组长度为0,直接返回另一个数组k-1位置上的数;

     c. 在b的前提下,如果k=1,说明2个数组的长度都为1,如果使用公式会出错,这时直接返回2个数组中较小的数即可。

 

以下是我写的代码,欢迎大牛指导交流~

AC,Runtime: 168 ms

//LeetCode_Median of Two Sorted Arrays
//Written by zhou
//2013.11.8
class Solution {
public:
    double findMedianSortedArrays(int A[], int m, int B[], int n) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        
        int size = m + n;
        int k = (m + n + 1) / 2;
        if (size & 0x1) //奇数
           return FindKth(A,m,B,n,k);
        else return (FindKth(A,m,B,n,k) + FindKth(A,m,B,n,k+1)) / 2;  //偶数
    }
    
    //找出第K个的数
    double FindKth(int *A, int m, int *B, int n, int k)  
    {
        
        if (m > n)    //一直默认A为较短的数组
           return FindKth(B, n, A, m, k);  
        if (m == 0)    //较短的数组为空
           return B[k -1];
        if (k == 1)  //k=1,直接比较A[0],B[0]
           return A[0] < B[0] ? A[0] : B[0];
        
        //计算K分为2部分,各在A和B中占多少
        int tmp = k / 2;
        int pa = tmp < m ? tmp : m;
        int pb = k - pa;
        
        if (A[pa - 1] < B[pb - 1])   
           return FindKth(A + pa, m - pa, B, n, pb);  //剩下的pb个数在A+pa的后面数据和B的全部数据中找
        else if (A[pa - 1] > B[pb - 1])
           return FindKth(A, m, B + pb, n - pb, pa);  //剩下的pa个数在B+pb的后面数据和A的全部数据中找
        else return A[pa - 1];    //相等,刚好
        
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值