堆的运用
堆
堆是一棵完全二叉树,一般将其分为大顶堆和小顶堆。小顶堆是指子结点的值大于父结点的值;大顶堆就是堆的子结点的值都小于父结点的值。
在实现的时候,常常使用基于数组的堆(由于是完全二叉树,所以元素在数组中是连续的)。如果一个结点的编号为n,那么,其对应的左右子结点的编号分别是2n和2n+1。如果一个结点的编号为n,则其父结点的编号为n/2。
- 添加操作
如果要添加一个元素,首先把这个元素放在最后,然后与其父结点比较,如果不满足堆的性质,则交换。如果不满足堆的性质,则继续进行下去即可。 - 删顶
首先把最后一个元素与顶交换,再删除最后一个元素。如果顶不满足堆的性质,则与左右儿子比较,与较小(大)的儿子交换,然后继续下去,直至得到一个正确的堆。
问题
如果一个文件内存储了10亿个商品的销量数据,请你在其中
找出前1000大的数据。
分析
用前1000个数据建立大小为1000的小顶堆,后面的数据与顶堆的第一个数据进行比较,若比第一个数据大,则交换,再顶堆排序得到新的小顶堆;否则舍弃该数据。
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int a[N]={0};
int main()
{
int deta,i,k,m=10;
memset(a,0,sizeof(a));
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
for(i = 1; i <= m; i++)
scanf("%d", &a[i]);
//小顶堆排序
for(i = 2; i <= m; i++)
{
k = i; //第k个元素沿着路径进行小顶堆排序
while(k > 0)
{
if(k/2 <= 0 || a[k] >= a[k/2])
break;
swap(a[k], a[k/2]);
k = k/2;
}
}
// for(i = 1; i <= m; i++)
// printf("%d ", a[i]);
// printf("\n");
while(scanf("%d", &deta)==1)
{
if(deta>a[1])
{
swap(a[1], deta);
k=1;
while(2*k<=m)
{
if(a[k]<=a[2*k]&&a[k]<=a[2*k+1]) break;
if(a[2*k]<a[2*k+1])
{
swap(a[k], a[2*k]);
k=2*k;
}
else
{
swap(a[k], a[2*k+1]);
k=2*k+1;
}
}
//检错:防止剩余的0参与交换
if(a[k/4]>a[k/2])
swap(a[k], a[k/2]);
}
// for(i = 1; i <= m; i++)
// printf("%d ", a[i]);
// printf("\n");
}
for(i = 1; i <= m; i++)
printf("%d ", a[i]);
return 0;
}
运行结果(取比较小的m=10来验证)
测试数据:89 85 91 13 29 68 48 94 49 47 48 85 79 51 99 1 81 76 1 51
运行结果:68 81 76 89 85 91 79 94 99 85