Median of Two Sorted Arrays C++

这道题目很多人认为是leetcode上最经典的题目之一,很早之前就见过这个题目,好像算法导论也有讲过这个题。题意是找两个排好序的数组的中位数,如果不去考虑时间空间复杂度,这个问题极其简单,用merge排序将一个数组归并到另一个数组中去,然后就可以立马找到中位数,不过这样的时间空间复杂度都比较高,其实,按照这个思路写出来的代码也可以AC,可见leetcode的AC标准也不是很严格。

那么这个题有没有比较省空间和时间的方法呢?接下来我先说一下我的两个思路:

思路1:找中位数,就是找第(m+n)/2的那个数(m,n是两个数组的长度)。既然这样,我拿两个数组的第一个元素来比较,找到比较大的那个,然后再拿比较大的那个元素去跟另一个数组的第二的元素比较,依此类推每次找到比较大的拿个就跟另一个数组中的下一个元素比较,当比较了(m+n)/2次后,就可以找到那个元素了。要完成这个算法,需要有两个指针来标识需要比较的元素,需要一个计数器计算比较了多少次,最后还需要有个标志来标记目前比较大的元素位于那个数组,以便去另一个数组找到下一个比较的元素,当一个数组遍历完了,比较次数还是不到m+n/2的话,假设当前比较大的数组下标为i,当前比较次数为k,那么这个元素所在的数组的第i+(m+n)/2-k,就是目标元素,当然还要根据m+n的奇偶情况来判断最后的结果。这个思路的时间复杂度是O(m+n)的,比之前的那个想法快很多,但是写起来比较麻烦,这里就不贴代码了(其实我写了好长时间都不能AC,边缘情况太多,这里就偷懒了)。

思路2:要求的时间复杂度是log(m+n),单纯这条要求我觉得就是解题的最大提示了,这种要求的时间复杂度,在数列问题里就等于直接告诉你用二分法嘛。思想很简单,我拿第一个数组的中位数与第二个数组的中位数比较,结果有三种情况:大于,小于,等于。等于就不说了,两个中位数相等的数列,归并起来的中位数肯定等于单个的中位数。

         我们主要说一说小于的情况(大小本来就是相对的,所以大于小于是一种情况),假设A[]有m个元素,B[]有n个元素,如果将两个数组归并起来得到一个C[]有m+n个元素(我们只是假设有这个数组的存在),现在比较A[m/2-1]跟B[n/2-1]这俩元素后,发现A[m/2-1]小,那么可以得出一个结论了:A前m/2 -1个元素都小于A[n/2-1],同样的B的后n/2-1个元素都大于A[m/2-1],那么C的中位数只会在A的后一半元素中或者B的前一半元素中,因为A前m/2-1个元素会在归并后出现在C的前(m+n)/2-1元素中,这个地方有点难理解,我多解释几句:A的中位数小于B的中位数,那么两个数组加起来比A中位数大的最少就有m/2+n/2+1个,那么A的中位数在归并后一定出现在C的前(m+n)/2-1元素中,那么比A中位数小的前m/2-1个元素也一定在C的前(m+n)/2-1元素中,同理可以推出C的后n/2的元素在C的后(m+n)/2-1元素中。

明白了这个推论后,这个问题貌似就解决了,因为每次比较都可以舍去一半的元素不用考虑,其实这个办法好说不好写,有毛毛多的边界情况,单是数组长度小于等于1的情况就够写半天的,所以这个算法我也写了好久不能AC。

两个思路写了好久都无法AC,最后无奈度娘各位前辈,得到了这个解决这类问题的万金油方法,其实看了这个方法之后,我忽然觉得整个人都不好了,为啥思路一样的,写成一般情况都比求中位数来的简单。下面我来说一下这个正解。

正解:求两个有序数列的第k小的数,其实理解了我上面解释的思路2的人,应该很好理解下面这段解释的。

同样假设A[]有m个元素,B[]有n个元素,如果将两个数组归并起来得到一个C[]有m+n个元素,现在我们找C中第k个元素,把问题类比到思路2的中位数,现在比较A[k/2-1],B[k/2-1],结果同样有三种情况,大于,小于,等于,等于的情况就不说了,直接返回就得了。如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中。证明也很简单,可以采用反证法。假设A[k/2-1]大于合并之后的第k小值,我们不妨假定其为第(k+1)小值。由于A[k/2-1]小于B[k/2-1],所以B[k/2-1]至少是第(k+2)小值。但实际上,在A中至多存在k/2-1个元素小于A[k/2-1],B中也至多存在k/2-1个元素小于A[k/2-1],所以小于A[k/2-1]的元素个数至多有k/2+ k/2-2,小于k,这与A[k/2-1]是第(k+1)的数矛盾。

问题解释到这里,应该算是解释清楚了,NO!NO!NO!重要的事情说三遍,经过我这段时间的刷题历程,终于在这个题上知道了我跟大神的差距远不止在思路上,还有在代码实现上。思路想明白了,算法实现时还需要考虑各种边界情况,还有函数的结构等等,就拿这个题目来说,我在算法实现时考虑到了各种例外情况都不适用我的核心函数,一个数组空的情况,两个数组都空的情况,一个数组长度等于1的情况,两个数组长度等于1的情况,洋洋洒洒的给代码整了六七十行,最后还是不能AC。看来以后要在解决问题的基础上尽量的精简代码才是王道。最后附上大神的代码,实测可以AC。

double findKth(int a[], int m, int b[], int n, int k)  
    {  
        if(n<m)  
            return findKth(b,n,a,m,k);  //保证m是较小的
        if(m==0)  
            return b[k-1];  
        if(k==1)  
            return min(a[0],b[0]);  
        int pa = min(m,k/2);  
        int pb = k - pa;  
        if(a[pa-1] < b[pb-1])  
            return findKth(a+pa,m-pa,b,n,k-pa); //这行调用尽显C++的优势,数组即指针,如果是java或
<span style="white-space:pre">						</span>//者C#还要附上一个开始下标,函数参数又增加两个 
        else if(a[pa-1] > b[pb-1])  
            return findKth(a,m,b+pb,n-pb,k-pb);  
        else  
            return a[pa-1];  
    }  
    double findMedianSortedArrays(int A[], int m, int B[], int n) {  
        // Note: The Solution object is instantiated only once.  
        int total = m+n;  
        if(total & 0x1)  
            return findKth(A,m,B,n,total/2+1);  
        else  
            return (findKth(A,m,B,n,total/2)+findKth(A,m,B,n,total/2+1))/2;  
    } 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值