首页 > 代码库 > 中位数

中位数

求给定整数数据中的第i小的数

如果数据量很大,不能一次读入内存,可以将数据分区间储存。具体而言,就是讲数据分为...-2^20~-1,0~2^20-1, 2^20~2*2^20-1,2*2^20~3*2^20-1....并统计每个区间有多少个数据。这样就可以判断第i小的数在哪个区间。并且可以判断它在该区间是第几小。

每个区间的大小是2^20,一共有2^32/2^20=2^12个区间,每个区间的数存放在一个文件中。2^20 * 4byte = 4MB,可以读入内存。

现在问题转化为了求4MB数据中的第j位数。

 

采用随机选取算法,最差时间复杂度为O(n^2),平均时间复杂度为O(n)

该算法的思路是,通过快速排序的分割方式,一次可以将数据分为两部分,主元最后的位置m就是一个m分割,前m个数小于主元,后面的数大于主元,主元是所有数中第m+1大的。如果m+1 = j,则问题就解决了。

如果m+1 < j,说明第j位数在第一次分割的后半部分,此时以m+1下标作为begin,以len-1作为end,继续分割。得到主元的位置s,则主元前面的s个数都小于主元,主元后面的数都大于主元,主元是所有数中的第s+1大。判断s+1和j的关系。

如果m+1>j,说明第j位数在第一次分割的前半部分,此时以下标0作为begin,以m-1作为end,继续分割。

最终可以得到j

注意,每次需要记录下begin和end。

/**求一组数据中的第i小的数*/#include<stdio.h>#include<stdlib.h>#include<time.h>int RandomPartition(int *A,int beg,int end){    int k = beg+rand()%(end-beg+1);    int temp = A[beg];    A[beg] = A[k];    A[k] = temp;        int s=A[beg];    int i=beg;    int j=end;    while(j>i)    {        while(A[j]>s && j>i)        {            j--;        }        if(j>i)        {            A[i]=A[j];            i++;        }        while(A[i]<s && i<j)        {            i++;        }        if(i<j)        {            A[j]=A[i];            j--;        }    }    A[i] = s;    return i;}int Median(int *A,int len,int i){    if(i>len || i<1)    {        printf("i>len\n");        exit(0);    }    else    {        int k;        int beg =0;        int end = len-1;        k=RandomPartition(A,beg,end);        while((k+1)!=i)        {            if((k+1)>i)            {                end = k-1;                k=RandomPartition(A,beg,end);            }            else            {                beg = k+1;                k=RandomPartition(A,beg,end);            }        }        return A[k];    }}int main(){    int A[]={1,45,43,65,23,42,63,22,18,9};    srand((unsigned int)time(NULL));    printf("%d\n",Median(A,10,2));    return 0;}

 

中位数