给定两个长度相同,分别有序的数组A和B,求两个数组中所有数的中位数

该题目比Leetcode中的题目Median of Two Sorted Arrays 稍简单,因为两个数组的长度相同。 

解法转自 http://www.cnblogs.com/qianye/archive/2013/05/06/3063980.html

给定两个数组A和B,数组的长度相同都为n,两个数组都分别有序,要求出两个数组中所有数的中位数。

分析:两个数组中总共有2n个数,有两个中位数,这里取较小的那一个。要找出2n个数的中位数,其实就是找出其中第n大的数,用两个下标p,q分别指向A,B两个数组中的第一个元素,如果A中元素较小就移动p,如果B中元素较小就移动q,并计录移动的次数,直到找到第n大的元素为止,总共的移动次数n-1,时间复杂度为O(n)

上述方法的时间复杂度已经比较好了,但是每次只移动一位,没有很好的利用两个数组分别有序的条件,下面提供一个O(logn)的方法。具体步骤:

每次取A数组和B数组的中间元素p和q,比较两个数的大小,如果p等于q,则p和q就是所有数字的中位数; 如果p大于q,则中位数只可能存在A数组的左半部分和B数组的右半部分; 如果p小于q,则中位数只可能存在于A数组的右半部分和B数组的左半部分,这样规模变为原来的一半。

同时有结论,A数组和B数组的子数组中求出的中位数为所有数字的中位数

证明:

  1.  假设A和B数组的长度为奇数2k+1, 取A数组的中间元素A[k],取B数组的中间元素B[k], 如果A[k]等于B[k],则A[k]必为第2k+1大的数,是所有数字的中位数。如果A[k]大于B[k],则A[k]至少也是第2k+2大的数,B[k]至多为第2k+1大的数,中位数介于B[k]和A[k]之间,因此不可能存在于A数组的右半部分和B数组的左半部分,有效数组的长度为k+1,原来的数字总共有4k+2个,现在变为2k+2个,可以轻易证明这2k+2个数中的中位数就是原来的4k+2个数的中位数,问题规模为原来的一半,当A[k]小于B[k] 的时候情况相似。
  2. 假设A和B数组的长度为偶数2k,每次取A数组中靠后的那个中间元素A[k], 取B数组靠前的那个中间元素B[k-1], 如果A[k]等于B[k-1],则A[k]必为第2k大的元素,是所有数字的中位数。如果A[k]大于B[k],则A[k]至少也是第2k+1大的数,B[k-1]至多是第2k大的数,中位数介于B[k-1]和A[k]之间,因此只可能出现在A数组的左半部分和B数组的右半部分,数组的长度为看k+1,原来的数字总共有4k个,现在变为2k+2个,可以证明这2k+2个数中的中位数就是原来4k个数的中位数,问题规模为原来的一半,当A[k]小于B[k-1]时情况类似。
  3. 每次求中位数的时候首先判断数组的长度,然后按照以上的两种规则缩小问题规模,每次都变为原来的一半,因此时间复杂度为O(logn).
  4. 注意当数组的长度变为2的时候,如果A中为67和72, B中为71和72,就会陷入死循环,需要另作判断
代码如下:
#include <iostream>
#include <assert.h>
using namespace std;

int MidNum(int *A,int l1,int r1,int *B,int l2,int r2)
{
    int mid1,mid2;
    assert(A != NULL && B!=NULL && l1<=r1 && l2<=r2 && r1-l1 == r2-l2);
    //当两个数组都只剩下一个元素的时候取比较小的数作为中位数
    if(l1==r1 && l2==r2)
        return A[l1]<B[l2]?A[l1]:B[l2];
    //当两个数组中都只剩下两个元素的时候,会出错,例如A中剩下63,72.B中剩
    //下71,72时
    if(r1-l1==1)
    {
        if(A[l1]<B[l2])
            return B[l2]<A[r1]?B[l2]:A[r1];
        else
            return A[l1]<B[r2]?A[l1]:B[r2];
    }
    //当数组的长度为偶数的时候,A数组中取靠后的那个中位数,B数组中取靠
    //前的那个中位数
    if((r1-l1+1)%2==0)
    {
        mid1=(r1+l1)/2+1;
        mid2=(r2+l2)/2;
    }
    else
    {
        mid1=(r1+l1)/2;
        mid2=(r2+l2)/2;
    }
    if(A[mid1]==B[mid2])
        return A[mid1];
    else if(A[mid1]>B[mid2])
        return MidNum(A,l1,mid1,B,mid2,r2);
    else
        return MidNum(A,mid1,r1,B,l2,mid2);
}

int main()
{
    int A[6]={1,3,5,6,8};
    int B[6]={2,4,6,9,11};

    int m2=MidNum(A,0,4,B,0,4);
    cout<<m2<<endl;

    int A2[10]={17,18,28,37,42,54,63,72,89,96};
    int B2[10]={3,51,71,72,91,111,121,131,141,1000};

    int m=MidNum(A2,0,9,B2,0,9);
    cout<<m<<endl;

    return 0;
}

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值