数据结构实验之排序四:寻找大富翁
Time Limit: 200 ms Memory Limit: 512 KiB
Problem Description
2015胡润全球财富榜调查显示,个人资产在1000万以上的高净值人
群达到200万人,假设给出N个人的个人资产值,请你快速找出排前M位的大富翁。
Input
首先输入两个正整数N( N ≤ 10^6)和M(M ≤ 10),其中N为总人数,M为需要找出的大富翁数目
接下来给出N个人的个人资产,以万元为单位,个人资产数字为正整数,数字间以空格分隔。
Output
一行数据,按降序输出资产排前M位的大富翁的个人资产值,数字间以空格分隔,行末不得有多
余空格。
Sample Input
6 3
12 6 56 23 188 60
Sample Output
188 60 56
先说一下这题的思路吧,首先,这道题如果直接对所有数据进行队排的话是会超时的,所以这里有一个更加巧妙的方法,就是我们可以先把前m个数据直接放进堆里,然后我们维护一个小顶堆,当输入完m个数据之后每当我们输入一个数据,我们拿这个数据与当前堆的堆顶(也就是堆里的最小值)进行对比,如果大于堆顶,就让把这个值赋值给堆顶,然后我们对这个堆进行调整,维护一个小顶堆,这样当所有值都输入完之后,这个小顶堆一定是最大的m个数,这时候我们只需要对这个堆进行排序就可以了。
堆排的思路(拿小顶堆来说,排序后是由大到小的顺序):
1.我们知道的是这个小顶堆的堆顶一定是整个堆里最小的值,我们把它和堆里面最后一个元素进行交换。
2.这样这个堆的最后一个元素就是最小的元素了。
3.然后我们把最后一个元素以外的堆再次调整为一个小顶堆。
4.重复上述操作。
#include<stdio.h>
#include<string.h>
int size,a[100000]; //size代表堆里面有多少元素
void adjust(int n) //调整函数(维护小顶堆)
{
int i,p,t;
for(i=1; i*2<=n; i=p) //这里的结束条件为当前节点没有儿子
{
if(a[i*2+1]<a[i*2]&&i*2+1<=n) //我们需要在左右儿子里面找到一个
p=i*2+1; //较小的值,并与该节点比较,注意
else //这里要判断是否有右儿子
p=i*2;
if(a[i]>a[p])
{
t=a[i];
a[i]=a[p];
a[p]=t;
}
else
break;
}
}
void cp(int x) //仅仅对于这题而言可以这样做,这里是m个数据之后每输入一个数据
{ //就进行比较,如果大于堆顶元素就进行赋值操作并维护
if(x>a[1])
{
a[1]=x;
adjust(size);
}
}
void hsort() //这里是最后一步,输入完所有数据之后,对这个小顶堆
{ //进行堆排序,因为题目要求从大到小输出
int i,t;
for(i=size; i>=1; i--)
{
t=a[i]; //前面思路里提到的堆排步骤的实现
a[i]=a[1];
a[1]=t;
adjust(i-1);
}
}
int main()
{
int n,m,i,x;
scanf("%d %d",&n,&m);
for(i=1; i<=m; i++)
{
scanf("%d",&x);
a[++size]=x;
}
adjust(size); //可能会想到,前m个数据根本不需要先调整,因为我们在后
for(i=m+1; i<=n; i++) //面插入一个数之后也会进行调整,所以这里是多余的,但
{ //仔细一想的话,如果我们在这里不进行调整的话,我们是无
scanf("%d",&x); //法保证后面的值与堆内最小值比较的。我们当然可以用插入函数
cp(x); //来避免此操作。
}
hsort(); //堆排
for(i=1;i<=m;i++)
{
if(i==m)
printf("%d\n",a[i]);
else
printf("%d ",a[i]);
}
return 0;
}
写的并不仔细,如果想看具体堆的操作可以看这里