注:引用郭炜老师的著作。
描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输出。
input:
第一行:包含一个整数n,表示数组的大小。n < 100000。
第二行:包含n个整数,表示数组的元素,整数之间以一个空格分。每个整数的绝对值不超过100000000。
第三行:包含一个整数m。m < n。
output:
从大到小输出前m大的数,数据间用逗号隔开。
解题思路:如果先排序再从搜索,那么时间复杂度就是:nlogn + m。速度达不到笔者的要求,那有没有更快的呢?
当然有了(不然到这不就结束了吗。笔者又怎么会舍得可爱的你就这么失望离开)。我们可以把前m大的数放在数组的最右边,然后对最后m个数排序输出就可以了。
关键:在O(n)时间内实现把前m大的数放在数组的最右面。
引入操作 arrangeRight(k): 把数组(或数组的一部分)前k大的
都弄到最右边
1)设key=a[0], 将key挪到适当位置,使得比key小的元素都在
key左边,比key大的元素都在key右边(线性时间完成)
2) 选择数组的前部或后部再进行 arrangeRight操作。
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 100001
using namespace std;
void swap(int &a,int &b){
int tmp = a;
a = b;
b = tmp;
}
void arrangeRight(int a[],int s,int e,int p)
{
if(s >= e)
return;
int k = a[s];
int i = s;
int j = e;
while(i != j){
while(j > i&&a[j] >= k){
--j;
}
swap(a[i],a[j]);
while(i < j&&a[i] <= k){
++i;
}
swap(a[i],a[j]);
}
int num = e-i+1;
if(num == p)
return;
else if(num > p){
arrangeRight(a,i+1,e,p);
}
else{
arrangeRight(a,s,i-1,p - num);
}
}
int main()
{
int n,m;
int a[maxn];
cin>>n;
for(int i = 0;i < n;i++){
cin>>a[i];
}
cin>>m;
arrangeRight(a,0,n-1,m);
sort(a+n-m,a+n,greater<int>() );
for(int i = n-m;i<n;i++){
cout<<a[i]<<endl;
}
return 0;
}