问题描述
给定一个数组包含n个元素,统计前m大的数并且把这m个数从大到小输
出
问题分析
如果先排序再找前m大的,时间复杂度为O(nlogn)
用分治处理:复杂度 O(n+mlogm)
思路:把前m大的都弄到数组最右边,然后对这最右边m个元素排序,
再输出
关键 :O(n)时间内实现把前m大的都弄到数组最右边
算法实现
/*
如何将前k大的都弄到最右边
1)设key=a[0], 将key挪到适当位置,使得比key小的元素都在
key左边,比key大的元素都在key右边(线性时间完成)
2) 选择数组的前部或后部再进行 arrangeRight操作
*/
#include<iostream>
#include<stdlib.h>
using namespace std;
//设k为a[0],将比k大的挪到k右边,返回k的位置下标
int QuickArray(int a[],int first,int end){
int i=first,j=end;
int k=a[first];
while(i<j){
while(i<j&&a[j]>=k){
j--;
}
if(i<j){
a[i]=a[j];
i++;
}
while(i<j&&a[i]<k){
i++;
}
if(i<j){
a[j]=a[i];
j--;
}
}
a[i]=k;
return i;
}
//对前m大的数快速排序,也可以用系统自带的 qsort() ,O(nlogn)
void QuickSort(int a[],int first,int end){
if(first<end){
int key=QuickArray(a,first,end);
QuickSort(a,key+1,end);
QuickSort(a,first,key-1);
}
}
//分治法:将k两边的数分配好,时间复杂度O(n)
void ArrangeRight(int a[],int first,int end,int k,int n){
int key=QuickArray(a,first,end);
int length=end-key; //长度不加元素a[key]
if(length==k){
return;
}else if(length<k){
ArrangeRight(a,first,key,k-length,n);
}else if(length>k){
ArrangeRight(a,key+1,end,k,n);
}
}
//test
int main(){
int a[8]={2,7,1,8,2,4,5,9};
int n=sizeof(a)/sizeof(int);
int k=2;
ArrangeRight(a,0,n-1,5,n);
QuickSort(a,n-k+1,n-1);
for(int i=n-1;i>=n-k;i--){
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}