输出前m的的数
例题一:输出前m大的数:
描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输出。
输入
第一行包含一个整数n,表示数组的大小。n < 100000。 第二行包含n个整数,表示数组的元素,整数之间以一个空格分开。每个整数的绝对值不超过100000000。 第三行包含一个整数m。m < n。
输出
从大到小输出前m大的数,每个数一行。
方法1:对全体的数字进行归并排序,输出前m大的数(O(nlogn))代码:
#include <iostream> using namespace std; int a[1000000],temp[1000000]; void Merge(int *a,int s,int e,int m,int *tmp){ int pb = 0; int p1 = s,p2 = e; while(p1<=m&&p2<=e){ if(a[p1]<a[p2]){ tmp[pb++] = a[p1++]; }else{ tmp[pb++] = a[p2++]; } } while(p1<=m){ tmp[pb++] = a[p1++]; } while(p2<=e){ tmp[pb++] = a[p2++]; } for(int i = 0;i<e-s+1;i++){ a[s+i] = tmp[i]; } } void MergeSort(int *a,int s,int e,int *tmp){ if(s < e) { int m = s+(e-s)/2; MergeSort(a,s,m,tmp); MergeSort(a,m+1,e,tmp); Merge(a,s,m,e,tmp); } } int main(){ int n,m; while(cin>>n>>m){ for(int i = 0;i<n;i++){ cin>>a[i]; } MergeSort(a,0,n-1,temp); for(int i = n-m;i<n;i++){ if(i == n-m){ cout<<a[i]; }else{ cout<<","<<a[i]; } } cout<<endl; } return 0; }
方法二:
用分治处理:复杂度 O(n+mlogm)
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边
引入操作 arrangeRight(k): 把数组(或数组的一部分)前k大的都弄到最右边
如何将前k大的都弄到最右边
1)设key=a[0], 将key挪到适当位置,使得比key小的元素都在key左边,比key大的元素都在key右边(线性时间完成)——也就是使用一次快速排序的过程
2) 选择数组的前部或后部再进行 arrangeRight操作
a = k done 表示完成此过程
a > k 对此a个元素再进行arrangeRigth(k)
a < k 对左边b个元素再进行arrangeRight(k-a)
(这个地方引入图像加深思考)

有a个数是比key大的,b个是比key更小的,现在开始比较前k个数在这个数组里面的分布,有三种情况,第一种情况就是k==a这个就是说明刚好前a个大刚好满足题目的要求,如果a>k说明只要对右边再次找arrangeRight(k),如果是a<k的话对左边b个元素再来右边占了a个,那左边就只有k-a个了(注意这个时候左边是一个整体了)。后面代码再作解释把
#include <iostream> using namespace std; int a[1000000],temp[1000000]; void Merge(int *a,int s,int e,int m,int *tmp){ int pb = 0; int p1 = s,p2 = e; while(p1<=m&&p2<=e){ if(a[p1]<a[p2]){ tmp[pb++] = a[p1++]; }else{ tmp[pb++] = a[p2++]; } } while(p1<=m){ tmp[pb++] = a[p1++]; } while(p2<=e){ tmp[pb++] = a[p2++]; } for(int i = 0;i<e-s+1;i++){ a[s+i] = tmp[i]; } } void MergeSort(int *a,int s,int e,int *tmp){ if(s < e) { int m = s+(e-s)/2; MergeSort(a,s,m,tmp); MergeSort(a,m+1,e,tmp); Merge(a,s,m,e,tmp); } } void arrangeRight(int *a,int s,int e,int k){ if(s>=e){ return 0; } int t = a[s]; int i =s,j = e; while(i != j){ while(i < j && t <= a[j]){ --j; } swap(a[i],a[j]); while(i<j && t >= a[i]){ i++; } swap(a[i],a[j]); } //这个i结束是在中间 int num = e-i+1; if(num == k){ return; } if(num>k){ arrangeRight(a,i+1,e,k); } if(num<k){ arrangeRight(a,s,i-1,k-num); } } int main(){ int n,m; while(cin>>n>>m){ for(int i = 0;i<n;i++){ cin>>a[i]; } arrangeRight(a,0,n-1,m); MergeSort(a,n-m,n-1,temp); for(int i = n-m;i<n;i++){ if(i == n-m){ cout<<a[i]; }else{ cout<<","<<a[i]; } } cout<<endl; } return 0; }
这个地方要注意一下,arrangeRight这个函数做的只是把所有前m大的数放在右边。一旦总数量到达了m就会return。只要数据规模足够大的话后面的速度就会比前面快很多。
代码参考:(92条消息) 输出前m大的数_Jamence的博客-CSDN博客

浙公网安备 33010602011771号