zoukankan      html  css  js  c++  java
  • 给定两个长度相同,分别有序的数组A和B,求两个数组中所有数的中位数

    给定两个数组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数组的子数组中求出的中位数为所有数字的中位数

    证明:

           假设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] 的时候情况相似。

           假设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]时情况类似。

      每次求中位数的时候首先判断数组的长度,然后按照以上的两种规则缩小问题规模,每次都变为原来的一半,因此时间复杂度为O(logn).

      注意当数组的长度变为2的时候,如果A中为67和72, B中为71和72,就会陷入死循环,需要另作判断

     1 #include <iostream>
     2 #include <assert.h>
     3 using namespace std;
     4 
     5 int MidNum(int *A,int l1,int r1,int *B,int l2,int r2)
     6 {
     7     int mid1,mid2;
     8     assert(A != NULL && B!=NULL && l1<=r1 && l2<=r2 && r1-l1 == r2-l2);
     9     //当两个数组都只剩下一个元素的时候取比较小的数作为中位数
    10     if(l1==r1 && l2==r2)
    11         return A[l1]<B[l2]?A[l1]:B[l2];
    12     //当两个数组中都只剩下两个元素的时候,会出错,例如A中剩下63,72.B中剩
    13     //下71,72时
    14     if(r1-l1==1)
    15     {
    16         if(A[l1]<B[l2])
    17             return B[l2]<A[r1]?B[l2]:A[r1];
    18         else
    19             return A[l1]<B[r2]?A[l1]:B[r2];
    20     }
    21     //当数组的长度为偶数的时候,A数组中取靠后的那个中位数,B数组中取靠
    22     //前的那个中位数
    23     if((r1-l1+1)%2==0)
    24     {
    25         mid1=(r1+l1)/2+1;
    26         mid2=(r2+l2)/2;
    27     }
    28     else
    29     {
    30         mid1=(r1+l1)/2;
    31         mid2=(r2+l2)/2;
    32     }
    33     if(A[mid1]==B[mid2])
    34         return A[mid1];
    35     else if(A[mid1]>B[mid2])
    36         return MidNum(A,l1,mid1,B,mid2,r2);
    37     else
    38         return MidNum(A,mid1,r1,B,l2,mid2);
    39 }
    40 
    41 int main()
    42 {
    43     int A[6]={1,3,5,6,8};
    44     int B[6]={2,4,6,9,11};
    45 
    46     int m2=MidNum(A,0,4,B,0,4);
    47     cout<<m2<<endl;
    48 
    49     int A2[10]={17,18,28,37,42,54,63,72,89,96};
    50     int B2[10]={3,51,71,72,91,111,121,131,141,1000};
    51 
    52     int m=MidNum(A2,0,9,B2,0,9);
    53     cout<<m<<endl;
    54 
    55     return 0;
    56 }
  • 相关阅读:
    递归与分治4
    递归与分治3
    递归与分治2
    递归与分治1
    枚举与递推3
    枚举与递推2
    求编译器中数的最值(c++)
    移动小球链表实现
    阶乘的精确值
    while((c = getchar()) != EOF)(键盘输入问题)
  • 原文地址:https://www.cnblogs.com/qianye/p/3063980.html
Copyright © 2011-2022 走看看