连续k个数的最大值java_大量数据取k个最大值并排序

需求是这样的,我们都知道,在信息检索中,经常要取top-k(一共k,而不是第k)个得分最大的文档,并且从大到小排序。

而且文档规模很大,最少也要上千万。

话说这是一道很可以拿来面试的题啊。

我们不考虑Hadoop神马的,就说说单机怎么搞。

最傻的做法就是把1000万个都存储下来,然后sort,然后取min(k, vec.size())。

这样有两个缺点:

1、内存占用非常大,其实我们只要保留最大的1000个,但这样就要保存N个。在1000万的测试中,它要占用68M内存之多。

2、速度慢虽然是快速排序,但我们实际只要前1000个,后面那些排序都是无用功了。

于是靠谱的做法就是构建一个小顶堆:

(1)当堆中元素数量小于k时, 向堆中加入元素并调整堆

(2)当堆中元素数量大于等于k时 ,将堆顶(显然是当前最小的)和新元素比较:

(a)如果新元素小,丢弃之,我们只要top k 最大的。

(b) 如果新元素比堆顶大,把堆顶用新元素换掉,然后调整堆。

好了,理论是上面这个过程,我们一般就不要裸写堆了,用c++ stl的pop_heap/make_heap/push_heap搞定之。注意它没有调整堆单独的函数,而是第一次make_heap,之后用成对的pop_heap/push_heap来完成堆维护(这两个操作不会改变堆大小)。而堆是构建在vector基础上的。

细节是:上述默认都是构造大顶堆,从小到大排序等。于是我们要子构造一个反向排序函数。

好了,再重复一下堆:

小顶堆:堆顶始终是最小的元素,排序时把最小的换到末尾,类似选择排序的过程。这样拍出来的是从大到小。

大顶堆:排序之后从小到大。

比较一下性能(1kw个数据,值域0~1kw):

算法/内存/时间:

(1)sort topk / 68MB / 11s

(2)heap / 3MB / 6s

内存优势非常明显。时间优势在这个规模可能还显示不出来,也可能是qsort的随机性能。

堆解决代码:

#include

#include

#include

using namespace std;

//type use long

typedef long TYPE;

//反向比较器,将make_heap等函数转化为小顶堆

bool rcmp (const TYPE &a,const TYPE &b)

{

return a>b;

}

//An vector for global, would keep at most k elements

vector vec;

TYPE k;

//recv elemnts and keep top K(big), select top k

void recv_keep_top_k(TYPE elem) {

if(vec.size()

vec.push_back(elem);

if(vec.size()>=k) {

make_heap(vec.begin(), vec.end(), rcmp);

}

} else {

// exceed k, need select

//compare to smallest

if(elem < vec.front() ) {

//small than heap's small, drop

return;

}

//bigger than smallest in heap, keep and replace the small one

pop_heap(vec.begin(), vec.end(), rcmp);

vec.back() = elem;

push_heap(vec.begin(), vec.end(), rcmp);

return;

}

}

int main () {

k = 100;

TYPE e;

while(cin>>e) {

recv_keep_top_k(e);

}

//sort from small to big

//Each time, the smallest one was swap to tail

if(vec.size()< k) {

make_heap(vec.begin(), vec.end(), rcmp);

}

sort_heap(vec.begin(),vec.end(), rcmp);

cout << "Final top " << k << endl;

for (unsigned i=0; i

cout << vec[i] << endl;

}

return 0;

}

对照组小白鼠(sort topK):

#include

#include

#include

using namespace std;

//type use long

typedef long TYPE;

bool rcmp (const TYPE &a,const TYPE &b)

{

return a>b;

}

//An vector for global, would keep at most k elements

vector vec;

TYPE k;

int main () {

k = 100;

TYPE e;

while(cin>>e) {

vec.push_back(e);

}

//sort from big to small

sort(vec.begin(), vec.end(), rcmp);

cout << "Final top " << k << endl;

for (unsigned i=0; i

cout << vec[i] << endl;

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值