1. 程式人生 > >找出N個整數中最大的K個數

找出N個整數中最大的K個數

《尋找N個元素中的前K個最大者》方法總結是在這裡看到的 /algorithm/20111105/314362.html ,我覺得解法二和解法四用得廣泛一些,程式設計實現了一下。

利用快速排序中的partition操作

經過partition後,pivot左邊的序列sa都大於pivot右邊的序列sb;

如果|sa|==K或者|sa|==K-1,則陣列的前K個元素就是最大的前K個元素,演算法終止;

如果|sa|<K-1,則從sb中尋找前K-|sa|-1大的元素;

如果|sa|>K,則從sa中尋找前K大的元素。

一次partition(arr,begin,end)操作的複雜度為end-begin,也就是O(N),最壞情況下一次partition操作只找到第1大的那個元素,則需要進行K次partition操作,總的複雜度為O(N*K)。平均情況下每次partition都把序列均分兩半,需要log(2,K)次partition操作,總的複雜度為O(N*log(2,K))。

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
using namespace std;
/*分割,pivot左邊的都比右邊的大*/
template<typename Comparable>
int partition(vector<Comparable> &a,int left,int right){
int begin=left;
int end=right;
Comparable pivot=a[left];
while(left<right){
while(left<right && a[right]<=pivot)
right--;
a[left]=a[right];
while(left<right && a[left]>=pivot)
left++;
a[right]=a[left];
}
a[left]=pivot;
return left-begin; //返回pivot左邊的元素個數
}
template
<typename Comparable>
void findKMax(vector<Comparable> &vec,int left,int right,int k){
if(k>right-left+1)
return;
//由於partition時,總是固定地選取首元素作為軸,所以事先打亂一下順序比較好,防止演算法退化
//random_shuffle(vec.begin()+left,vec.begin()+right);
int n=partition(vec,left,right);
if(n==k || n==k-1)
return;
if(n<k-1)
findKMax(vec,left+n+1,right,k-n-1);
else if(n>k)
findKMax(vec,left,left+n-1,k);
}
int main(){
int total=5;//原先有5個元素
int k=3;//選取前3個最大的
srand(time(0));
vector<int> a(total);
for(int i=0;i<a.size();i++)
a[i]=rand()%100;
for(int i=0;i<a.size();i++)
cout<<a[i]<<"\t";
cout<<endl;
findKMax(a,0,a.size()-1,k);
for(int i=0;i<a.size();i++)
cout<<a[i]<<"\t";
cout<<endl;
return 0;
}

利用小根堆實現

順序讀取陣列中的前K個元素,構建小根堆。小根堆的特點是根元素最小,並且一次調整(deleteMin)操作的時間複雜度為log(2,K)。

接下來從陣列中取下一個元素,如果該元素不比堆頂元素大,則丟棄;否則用它替換堆頂元素,然後調整小根堆。

當把陣列中的元素全部讀出來後,小根堆中保留的就是前K大的元素。

初始建堆操作需要K*log(2,K)--這是最多的操作次數,從陣列中讀取後N-K個元素和堆頂元素一一比較,最壞的情況是每次都要替換堆頂元素,都要調整小根堆,複雜度為(N-K)*log(2,K)。總的複雜度為O(N*log(2,K))。

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<vector>
using namespace std;
template<typename Comparable>
void percolate(vector<Comparable> &vec,int index){
int i=index;
int j=2*i+1;
while(j<=vec.size()){
if(j<vec.size() && vec[j]>vec[j+1])
j++;
if(vec[i]<vec[j])
break;
else{
swap(vec[i],vec[j]);
i=j;
j=2*i+1;
}
}
}
template<typename Comparable>
void buildHeap(vector<Comparable> &vec){
int len=vec.size();
for(int i=(len-1)/2;i>=0;i--)
percolate(vec,i);
}
int main(){
srand(time(0));
vector<int> a(10); //原先有10個元素
for(int i=0;i<a.size();i++)
a[i]=rand()%100;
vector<int> b(7); //找出a中最大的前7個元素
for(int i=0;i<b.size();i++)
b[i]=a[i];
buildHeap(b);
for(int i=b.size();i<a.size();i++){
if(a[i]>b[0]){
b[0]=a[i];
percolate(b,0);
}
}
vector<int>::iterator iter=a.begin();
while(iter!=a.end()){
cout<<*iter<<"\t";
iter++;
}
cout<<endl;
iter=b.begin();
while(iter!=b.end()){
cout<<*iter<<"\t";
iter++;
}
cout<<endl;
return 0;
}