题目描述
给你n个整数,请按从大到小的顺序输出其中前m大的数。
输入
每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含n个各不相同,且都处于区间[-500000,500000]的整数。
输出
对每组测试数据按从大到小的顺序输出前m大的数。
样例输入
5 3 3 -35 92 213 -644
样例输出
213 92 3
这是算法课的第一道OJ题,我的第一想法是利用冒泡排序把n个整数由大到小排序,再利用for循环输出前m个数字,虽然能实现题目要求,但是时间复杂度达到了O(),效率太低
#include <iostream>
using namespace std;
void sort(int n,int m)
{
int a[1000] = { 0 };
for (int k = 0; k < n; k++)
{
cin >> a[k];
}
for (int i = 0; i < n - 1; i++) //冒泡排序
{
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] < a[j + 1])
{
swap(a[j], a[j + 1]); //大一的时候都用的中间变量temp来交换,这个swap()就很方便
}
}
}
for (int z = 0; z < m; z++)
{
cout << a[z] << ' '; //输出排序后的前m个数即可
}
}
int main()
{
int n, m;
cin >> n >> m;
sort(n, m);
return 0;
}
改良算法:散列表(Hash)
这种数据结构只是在纸上手写模拟过,这是第一次用代码实现。
思路:
因为n个数都是区间[-500000,500000]的整数(用i表示其中某一个数)。我们把i+500000,则得到了一个区间在[0,1000000]的整数,正好符合数组下标是大于等于0的整数的规则,我们建立一个数组a[1000000],遍历这n个数所在的数组,每遍历到一个i,则使数组a的a[i+500000]这个元素变成1(其余元素都是0)。然后从后往前(a[999999]到a[0])遍历数组a(即这个只有0和1的数组),当访问到1,输出这个元素的(下标-500000),即为i的值,当访问了m个1的元素,则也输出了前m个最大的元素了。
注意:
本题说明了n个数字各不相同,所以可以这样,否则如果n个数字中有两个相同,a[i+500000]这个元素已经变成1了,再遍历到第二个这个相同数值的i时,a[i+500000]又赋值为1,这样是感知不到有两个相同数值的i的。若题目说n个数中的数字可以有重复,则要对思路进行一些小改动,后面会介绍。
这种方法时间复杂度达到了O(n),比冒泡排序快很多
#include <iostream>
using namespace std;
int a[1000000] = { 0 }; //一定要放在外面
void sort1(int n, int m)
{
int i;
for (int k = 0; k < n; k++)
{
cin >> i; //输入n个数
a[i + 500000] = 1; //散列表a中,以输入的数+500000为下标的元素赋值为1
}
for (int j = 999999; j > 0; j--) //从后往前遍历散列表,实现从大到小输出
{
while (m > 0 && a[j] == 1)
{
m--; //有一个数满足条件,则m减1,减到零则输出了m个数了
cout << j - 500000 << ' ';
a[j] = 0; //输出后记得把a[j]赋值回0,否则同一个数会一直输出
}
}
}
int main()
{
int n, m;
cin >> n >> m;
sort1(n, m);
return 0;
}
若加上n个数中可能会有数字重复的限定条件?
这时只需要把把散列表元素赋值为1这一操作变成对相应散列表元素值进行自增(若一个数字出现了两次,散列表对应元素的值就是2),然后从后往前遍历的时候的判定条件就是a[j]!=0,每一次满足条件就a[j]--,而不是直接把a[j]赋值回0
#include <iostream>
using namespace std;
int a[1000000] = { 0 }; //一定要放在外面
void sort1(int n, int m)
{
int i;
for (int k = 0; k < n; k++)
{
cin >> i; //输入n个数
a[i + 500000]++; //散列表a中,以输入的数+500000为下标的元素赋值为1
}
for (int j = 999999; j > 0; j--) //从后往前遍历散列表,实现从大到小输出
{
while (m > 0 && a[j]!=0)
{
m--; //有一个数满足条件,则m减1,减到零则输出了m个数了
cout << j - 500000 << ' ';
a[j]--; //输出后把a[j]的值减去1,若此时a[j]没到0,则会继续输出这个数字
}
}
}
int main()
{
int n, m;
cin >> n >> m;
sort1(n, m);
return 0;
}
这段代码和上一题的改变就在a[i + 500000] = 1 变为了 a[i + 500000]++。 a[j]==1变为了a[j]!=0。a[j] = 0变为了a[j]--。 三处
这段代码是兼容上一题的,而且考虑了重复的情况,更加成熟。
样例输入:
5 3
3 -35 92 213 92
样例输出:
213 92 92