您的位置:首页 > 其它

10G个整数,乱序排列,要求找出中位数。内存限制为 2G。只写出思路即可

2013-04-28 17:20 399 查看
参考:http://hi.baidu.com/xzufpnrqufbavxq/item/eb6d77c72b25da06ac092f29

借鉴桶排序思想

第一步:因为整数为32位,我们可以按照整数的高16位划分桶进行计数,2^16=64k,即可划分成64k个桶,也就是大小为2^16的数组。这里存在一个问题,如果数组类型是int,能够计数的最大值是2^32=4G,而如果10G个整数完全相同,则int类型表示不了10G个数,所以这里数组类型采用long long 8字节类型,占用内存为2^16*8B=518KB,远小于提供的2G内存,造成内存浪费,所以我们可以这样改进:分成2G/8B = 2^28 =256M段,其中2G是可用内存大小,8B是数组类型大小,我们可以依据整数的高28位来进行计数,这样划分的段越多,第二部扫描分析的数据就越少。

long long counter[1<<28];//256M个桶
unsigned int x;
memset(counter,0,sizeof(counter));
foreachnumber(x)//遍历每个整数
{
counter[x>>4]++;//高28位指向的桶计数加1
}
long long sum = 0;//记录桶计数的总和
for(i=0;i<1<<28;i++)
{
sum += counter[i];
if(sum>=5LL<<30)//找到中位数所在的桶
break;
}
sum -= 5LL<<30;//sum代表5LL<<30到counter[i]桶末端的个数
sum = counter[i] - sum;//sum代表counter[i]桶初端到5LL<<30的个数


第二部:前一步已经把10G数据按照高28位分到了256M桶中,且已经找到中位数在哪一段,只要把此段按照低4位分到16个段中,即可找到中位数

int segment = i;//中位数所在的段
memset(counter,0,sizeof(counter));
foreachnumber(x)//再遍历一次10G整数
{
if((x>>4)==i)
counter[x&(~((-1)<<4))]++;//按照低4位找到桶,然后计数加1,-1的8位二进制表示为11111111
}
long long lsum = 0;
for(i=0;i<1<<4;i++)
{
lsum += counter[i];
if(lsum>=sum)//找到中位数
break;
}
int keynum = (segment<<4)| (i);//高28位和低4位组合成中位数

总共只要读两遍整数,对每个整数也只是常数时间的操作,总体来说是线性时间

若是有符号的整数,只需要改变映射即可
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐