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

参考: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位组合成中位数

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

若是有符号的整数,只需要改变映射即可

 

 

 

你可能感兴趣的:(数据结构与算法)