问题1:从100亿个数据中找出前100个数据
分析:由于只要找出前100个数据,可以采用堆来进行处理。堆特别适合的场景是从海量数据中找出前m个最大值或最小值,当m不大的时候效率很高。
步骤:
1.读取前100个数据,建立最小堆(最小堆的作用是方便找出目前最大的100个数据中的最小值,把待比较的数据与这个最小值比较就好。由于一次性只读取100个数据,不需要一次性读完所有数据,降低对内存的要求)
2.对余下数据依次进行读取,分别与堆中最小数据进行比较。若当前数据小于堆中最小数据,说明当前数据在前100个以外,即舍弃。若当前数据大于堆中最小数据,则从堆中pop出最小数据,向堆中插入当前数据,并调整堆。
3.最后剩下的100个堆中元素即为所求。
问题2:从100亿个数据中找出前1亿个数据
分析:这个问题和上个问题有所不同。假如还用最小堆法,那么维护一个一亿数据量的堆是非常不高效的,而且堆的空间会很大,占用非常多的内存。可以考虑一下几种方法。
1.位图法(假如每个数据最多重复一次)
首先对内存占用进行分析,假如说每个数据不大于100亿,
而且最多重复一次,那么我们一共需要100亿位。
1byte = 8bit(位)
1024byte = 8*1024 = 1k
1M = 1024k = 8*1024*1024 = 8388608bit
那么100亿位就是100*10^8/8388608 = 1187M,大约需要1G多的空间。
2.快排中的partition方法
因为是寻找前k大个数,只要找到分割整个数据的第k个数据就好。
//快速排序的划分函数
int partition(int v[], int left, int right)
{
int pivotpos = left, pivot = v[left];
for(int i=left+1;i<=right;++i)
if (v[i] < pivot)
{
++pivotpos;
if (pivotpos != i)
swap(v[i], v[pivotpos]);
}
v[left] = v[pivotpos];
v[pivotpos] = pivot;
return pivotpos;
}
//线性寻找第k大的数
int select(int v[], int left, int right, int k)
{
if (left > right || k < 0 ) return -1;
int pivotpos = partition(v, left, right);
if (pivotpos > k)
return select(v, left, pivotpos - 1, k);
else if (pivotpos < k)
return select(v, pivotpos + 1, right, k);
else
return k;
}