题目描述:给你n个整数,请按从小到大的顺序输出其中前m大的数。
输入:每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含各不相同的,且大小处于[-500000,500000]的整数。
输出:对每组数据按从大到小的顺序输出前m大的数。
样例输入:
5 3
3 -35 92 213 -644
样例输出:
213 92 3
思考:在本例中,如果用排序来解决该题,由于待排序数字的数量十分庞大(1000000),即使使用时间复杂度为O(nlogn)的快排,其时间复杂度也达到了千万数量级,因此不能使用快排解决此题。
思路:由于输入数据中出现了负数,于是我们不能直接把输入数据当作Hash数组的下标来访问数组,而是将每一个输入的数据都加上一个固定的偏移值OFFSET,使得输入数据[-500000,500000]区间被映射到Hash数组下标的[0,1000000]区间。我们利用hash数组将所有出现过的数字(对应的数组元素,Hash的下标)都标记为1,当我们需要输出前m大个元素时,我们只需要从500000开始,降序遍历这个数组,,查找前m个标记成1的数组元素,输出其对应的下标数字即可。
#include <stdio.h>
#include <stdlib.h>
#define OFFSET 500000 //偏移量,用于补偿实际数字与数组下标之间偏移量
int Hash[1000001];//Hash数组,记录每个数字是否出现,不出现为0,出现为1
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
int i;
for(i= -500000;i<=500000;i++){
Hash[i+OFFSET] = 0;
}//初始化,将每个数字都标记为未出现
for(i=0;i<n;i++){
int x;
scanf("%d",&x);
Hash[x +OFFSET]=1;//凡是出现过的数字,该数组元素均为1
}
for(i=500000;i>=-500000;i--){ //输出前m个数,从Hash数组最末端判断
if(Hash[i+OFFSET]==1){ //若出现过,在m不等于0之前,一定是前m个大数
printf("%d",i); //输出该数字
m--; //输出一个数字后,m--,直至变为0
if(m!=0) printf(" "); //注意格式,求m个数未被输出完毕,在输出的数字后紧跟一个空格
else{
printf("\n");//若m个数字已经被输出完毕,则在输出的数字后面输出换行,并跳出程序
break;
}
}
}
}
return 0;
}
注意:在输出的每一个数字之间存在一个空格,而在最后一个数字之后却不存在空格。
输入:每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含相同的,可能有重复的数字,且大小处于[-500000,500000]的整数。
#include <stdio.h>
#include <stdlib.h>
#define OFFSET 500000
int Hash[1000001];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
int i;
for(i= -500000;i<=500000;i++){
Hash[i+OFFSET] = 0;
}
for(i=0;i<n;i++){
int x;
scanf("%d",&x);
Hash[x +OFFSET]++;
}
for(i=500000;i>=-500000;i--){
while(m>0&&Hash[i+OFFSET]--){
printf("%d",i);
m--;
if(m!=0) printf(" ");
else{
printf("\n");
break;
}
}
}
}
return 0;
}