在我还在礼拜六工作的时候,游戏后台需要实现一个给用户发送周报的功能,所谓发周报就是指抽出某项数据排名前十的用户,当时是配合mongodb数据库写的。当初我选用的就是小根堆的排序算法。
这个问题我当时是这么考虑的:用户可能很多,而且只想要得到的是前十名,那么完全没有必要去给所有用户进行排序,所以考虑分为两步:1)找出前十大的数据;2)对这10个数据进行排序并写入数据库(这个第二部非常简单了,因为是常数数目,冒泡排序都可以)。
所以重点呢就在第一步如何实现了。小根堆是解决这个问题的一个非常好的方法:首先,直接选择前10个数据,并建立小根堆,这样,堆顶就是这10个中最小的元素;再接着扫描下面的数据,并与堆顶元素作比较,如果比堆顶小,就忽略继续扫描下一个,如果比堆顶元素大,则替换堆顶元素,并对堆进行重新排列,恢复小根堆。这样只要扫描一遍,就能得到所有的数据中最大的10个元素了。
当时我是用python写的,现在再写个C语言的实现版本吧:
//date:2012/9/14
//author:Andy
//description:小根堆的妙用
#include <stdio.h>
#include <stdlib.h>
void build_small_root_heap(int *,int);
void small_root_heapify(int *,int,int);
void get_top_ten(int *,int);
int b[10];//set a place for top 10
main()
{
int a[20] = {2,3,5,9,10,12,42,23,23,212,15,15,8,90,10,24,45,544534,34,34};//raw data
int length = 20;
int i;
printf("\n The original array is :\n");
for(i = 0;i < length;i++)
printf("%d ",a[i]);
get_top_ten(&a[0],length);
build_small_root_heap(&a[0],length);
printf("\n The top 10 are :\n");
for(i = 0;i < 10;i++)
printf("%d ",b[i]);
system("pause");
return 0;
}
void get_top_ten(int* a,int length){
int i = 0;
int temp;
for(i = 0;i < 10;i++){
b[i] = a[i];
}
build_small_root_heap(&b[0],10);
for (;i < length;i++){
if (b[0] < a[i]){
b[0] = a[i];
small_root_heapify(&b[0],0,10);//if the top of the small root heap is less than the next element,replace it and small_root_heapify the heap again
}
}
}
void build_small_root_heap(int* a,int l){
int start = l / 2;
for (;start > 0;start--)
small_root_heapify(&a[0],start,l);
}
void small_root_heapify(int* a,int i,int length){
int l = 2*i+1;
int r = 2*i+2;
int smallest;
int temp;
if(l < length && a[l] < a[i])
smallest = l;
else
smallest = i;
if(r < length && a[r] < a[smallest])
smallest = r;
if(i != smallest){
temp = a[i];
a[i] = a[smallest];
a[smallest] = temp;
small_root_heapify(&a[0],smallest,length);
}
}
ok,对top 10的再排序就不写了。。。
这个问题我当时是这么考虑的:用户可能很多,而且只想要得到的是前十名,那么完全没有必要去给所有用户进行排序,所以考虑分为两步:1)找出前十大的数据;2)对这10个数据进行排序并写入数据库(这个第二部非常简单了,因为是常数数目,冒泡排序都可以)。
所以重点呢就在第一步如何实现了。小根堆是解决这个问题的一个非常好的方法:首先,直接选择前10个数据,并建立小根堆,这样,堆顶就是这10个中最小的元素;再接着扫描下面的数据,并与堆顶元素作比较,如果比堆顶小,就忽略继续扫描下一个,如果比堆顶元素大,则替换堆顶元素,并对堆进行重新排列,恢复小根堆。这样只要扫描一遍,就能得到所有的数据中最大的10个元素了。
当时我是用python写的,现在再写个C语言的实现版本吧:
//date:2012/9/14
//author:Andy
//description:小根堆的妙用
#include <stdio.h>
#include <stdlib.h>
void build_small_root_heap(int *,int);
void small_root_heapify(int *,int,int);
void get_top_ten(int *,int);
int b[10];//set a place for top 10
main()
{
int a[20] = {2,3,5,9,10,12,42,23,23,212,15,15,8,90,10,24,45,544534,34,34};//raw data
int length = 20;
int i;
printf("\n The original array is :\n");
for(i = 0;i < length;i++)
printf("%d ",a[i]);
get_top_ten(&a[0],length);
build_small_root_heap(&a[0],length);
printf("\n The top 10 are :\n");
for(i = 0;i < 10;i++)
printf("%d ",b[i]);
system("pause");
return 0;
}
void get_top_ten(int* a,int length){
int i = 0;
int temp;
for(i = 0;i < 10;i++){
b[i] = a[i];
}
build_small_root_heap(&b[0],10);
for (;i < length;i++){
if (b[0] < a[i]){
b[0] = a[i];
small_root_heapify(&b[0],0,10);//if the top of the small root heap is less than the next element,replace it and small_root_heapify the heap again
}
}
}
void build_small_root_heap(int* a,int l){
int start = l / 2;
for (;start > 0;start--)
small_root_heapify(&a[0],start,l);
}
void small_root_heapify(int* a,int i,int length){
int l = 2*i+1;
int r = 2*i+2;
int smallest;
int temp;
if(l < length && a[l] < a[i])
smallest = l;
else
smallest = i;
if(r < length && a[r] < a[smallest])
smallest = r;
if(i != smallest){
temp = a[i];
a[i] = a[smallest];
a[smallest] = temp;
small_root_heapify(&a[0],smallest,length);
}
}
ok,对top 10的再排序就不写了。。。