您的位置:首页 > 其它

输入 n 个整数,输出其中最小的 k 个—7种方法实现

2014-07-23 16:45 471 查看

输入 n 个整数的一维数组,输出其中最小的 k 个

在面试时问到这个问题时,要注意时间复杂度,空间复杂度。大神JULY在http://blog.csdn.net/v_JULY_v/article/details/6370650中详细讲解了多种方法,我这里只写几种简单点的方法。

1、利用STL库中的partial_sort函数,对原数组 部分排序

函数原型partial_sort(beg,mid,end,comp)
//利用STL中的partial函数
void Search(int *a,int n,int k)
{

int *b=new int
;
for(int i=0;i<n;i++)
b[i]=a[i];
partial_sort(b,b+k,b+n,less<int>());
for(int i=0;i<k;i++)
cout<<b[i]<<endl;
delete b;
}

2、利用快速排序对数组排序

利用快速排序对数组排序,时间复杂度为O(n*log2n),空间复杂度为O(log2n)因为是用的递归方法,所以数据不能太大,否则会栈崩溃。
void QuickSort(int a[],int first,int last)
{
if(first<last)
{
int i=first,j=last;
while(i!=j)
{
if(i<j)//抓大放小
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
swap(i,j);
j++;
}
else
{
j--;
}
}
if(i>j)//抓小放大
{
if(a[i]<a[j])
{
swap(a[i],a[j]);
swap(i,j);
j--;
}
else
{
j++;
}
}
}
QuickSort(a,first,i-1);
QuickSort(a,i+1,last);
}
}

3、选择排序

每次选择最小值,只要k次即可,后面的无需再排。时间复杂度为O(n*k),空间复杂度为O(1)
<span style="font-size:18px;">void SelectSort(int a[],int n,int k)
{

for(int i=0;i<k;i++)//只需要排k次
{
int min=a[i],pos=i;
//从i+1到n之间找最小值
for(int j=i+1;j<n;j++)
{
if(min>a[j])
{
min=a[j];
pos=j;
}
}
a[pos]=a[i];
a[i]=min;
}

}</span>

4、利用堆排序,建立n个元素的小顶堆

利用堆排序,建立小顶堆,每次取堆首即最小值,取k次即可。时间复杂度为O(k*log2n),空间复杂度为O(1)。如果不允许修改原数组的话,需要拷贝一份,空间复杂度为O(n)。
<span style="font-size:18px;">void Sift_min(int a[],int i,int n)
{
int j=2*i+1;
while(j<n)
{
//j指向较小的
if(j+1<n && a[j]>a[j+1])
j++;
if(a[i]<a[j])
break;
else
{
swap(a[i],a[j]);
i=j;
j=2*i+1;
}
}
}
void MakeHeap_min(int a[],int n)
{
for(int i=(n-1-1)/2;i>=0;i--)
{
Sift_min(a,i,n);
}
}
void HeapSort_k(int a[],int n,int k)
{
MakeHeap_min(a,n);
for(int i=0;i<k;i++)//这里只要K次
{
swap(a[0],a[n-1-i]);
Sift_min(a,0,n-1-i);
}
}</span>

5、堆排序,但是建立k个元素的大顶堆

用容量为 k 的最大堆存储最先遍历到的 k 个数,并假设它们即是最小的 k 个数,建堆费时 O(k)后,有k1<k2<...<kmax(kmax 设为大顶堆中最大元素)。继续遍历数列,每次遍历一个元素 x,与堆顶元素比较,x<kmax,更新堆(用时 logk),否则不更新堆。这样下来,总费时 O(k+(n-k)*logk)=O(n*logk)。此方法得益于在堆中,查找等各项操作时间复杂度均为 logk。
时间复杂度为O(n*log2k),空间复杂度为O(k)。
<span style="font-size:18px;">void Sift_max(int a[],int i,int n)
{
int j=2*i+1;
while(j<n)
{
//j指向较小的
if(j+1<n && a[j]<a[j+1])
j++;
if(a[i]>a[j])
break;
else
{
swap(a[i],a[j]);
i=j;
j=2*i+1;
}
}
}
void Heap_k( int a[],int b[],int n,int k)
{
//继续遍历数列,每次遍历一个元素 x,与堆顶元素比较,x<kmax,更新堆(用时 logk),否则不更新堆
for(int i=k;i<n;i++)
{
if(b[0]>a[i])
{
b[0]=a[i];
Sift_max(b,0,k);
}
}
//最终的k个元素不一定是有序的,若要最终的b[]中的k个元素从小到大排序,用排序算法再排即可
}

//建立长度为k的堆
int b[k]={0};
for(int i=0;i<k;i++)
{
b[i]=a[i];
}
for(int i=(k-1-1)/2;i>=0;i--)
{
Sift_max(b,i,k);
}
//遍历,比较,更新
Heap_k(a,b,n,k);

for(int i=0;i<k;i++)
{
cout<<b[i]<<' ';
}</span>

6、计数排序

如果N个数都是正数,取值范围不太大,可以考虑用空间换时间。申请一个包括N中最大值的MAX大小的数组count[MAX],count[i]表示整数i在所有整数中的个数。这样只要扫描一遍数组,就可以得到第K大的元素。时间复杂度为O(n+MAX),空间复杂度为O(MAX)
void Search_k(int a[],int b[],int n,int k,int MAX)
{

int *sum=new int[MAX+1];
for(int i=0;i<=MAX;i++)
{
sum[i]=0;
}
//统计0-MAX中各个数出现的次数
for(int i=0;i<n;i++)
{
sum[a[i]]++;

}

//从0-MAX中从0开始选出k个数
int j=0;//用来计k个数
for(int i=0;i<MAX+1;i++)
{
if(j>=k)
break;
if(sum[i]>0)//只有出现的数才行
{
for(int m=0;m<sum[i];m++)//出现次数sum[i],赋值sum[i]次
{

if(j<k)//防止最后一个值赋值越界
b[j]=i;
else
return ;//赋值结束了
j++;
}
}
}
delete []sum;
}

7、类似partition的快排

假设N个数存储在数组S中,从数组中随机找一个元素X,将数组分成两部分Sa和Sb.Sa中的元素大于等于X,Sb中的元素小于X。Sa是包含关键元素的。出现如下两种情况:(1)若Sa组的个数大于或等于K,则继续在sa分组中找取最大的K个数字
。(2)若Sa组中的数字小于K
,其个数为T,则继续在sb中找取 K-T个数字 。一直这样递归下去,不断把问题分解成小问题,平均时间复杂度为O(N*logK)

<span style="font-weight: normal;">void QuickSort_k(int a[],int first,int last,int k)
{
if(first<last)
{
int i=first,j=last;
while(i!=j)
{
if(i<j)//抓大放小
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
swap(i,j);
j++;
}
else
j--;

}
if(i>j)
{
if(a[i]<a[j])
{
swap(a[i],a[j]);
swap(i,j);
j--;
}
else
j++;

}
}
int len_a=i-first+1;
int len_b=last-(i+1)+1;
if(len_a>=k)
QuickSort_k(a,first,i-1,k);
else
{
QuickSort_k(a,i+1,last,k-len_a);
}
}
}</span>

总结:

第4、5中方法的比较:
采取建立 n 个元素的最小堆后取其前 k个数的方法的复杂度小于采取常规的建立
k 个元素最大堆后通过比较寻找最小的 k 个数的方法的复杂度。
但,最重要的是,如果建立 n 个元素的最小堆的话,那么其空间复杂度势必为 O(N)(这里我觉得可能是拷贝了原来的一份数组),而建立 k 个元素的最大堆的空间复杂度为 O(k)。所以,综合考虑,我们一般还是选择用建立 k 个元素的最大堆的方法解决此类寻找最小的
k 个数的问题。第6中方法用法受限制。
第7种方法好于快排。
所以,第5种方法最好。附程序:
//输入 n 个整数,输出其中最小的 k 个。
#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>
using namespace std;//1、利用STL中的partial函数
void Search(int *a,int n,int k)
{int *b=new int
;
for(int i=0;i<n;i++)
b[i]=a[i];
partial_sort(b,b+k,b+n,less<int>());
for(int i=0;i<k;i++)
cout<<b[i]<<endl;
delete b;
}//2、快速排序 O(nlog2n)
void QuickSort(int a[],int first,int last)
{
int i,j;
if(first<last)
{
i=first,j=last;
while(i!=j)
{
if(i<j)//抓大放小
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
swap(i,j);
j++;
}
else
{
j--;
}
}
if(i>j)//抓小放大
{
if(a[i]<a[j])
{
swap(a[i],a[j]);
swap(i,j);
j--;
}
else
{
j++;
}
}
}
QuickSort(a,first,i-1);
QuickSort(a,i+1,last);
}
}//3、选择排序,只要找出最小的K个就好了,后面的不需要排了 O(n*k)
void SelectSort(int a[],int n,int k)
{for(int i=0;i<k;i++)//只需要排k次
{
int min=a[i],pos=i;
//从i+1到n之间找最小值
for(int j=i+1;j<n;j++)
{
if(min>a[j])
{
min=a[j];
pos=j;
}
}
a[pos]=a[i];
a[i]=min;
}}//4、建立小顶堆,取其堆首k次, O(k*log2n).因为建立堆的复杂度为O(n),共k次
void Sift_min(int a[],int i,int n)
{
int j=2*i+1;
while(j<n)
{
//j指向较小的
if(j+1<n && a[j]>a[j+1])
j++;
if(a[i]<a[j])
break;
else
{
swap(a[i],a[j]);
i=j;
j=2*i+1;
}
}
}
void MakeHeap_min(int a[],int n)
{
for(int i=(n-1-1)/2;i>=0;i--)
{
Sift_min(a,i,n);
}
}
void HeapSort_k(int a[],int n,int k)
{
MakeHeap_min(a,n);
for(int i=0;i<k;i++)//这里只要K次
{
swap(a[0],a[n-1-i]);
Sift_min(a,0,n-1-i);
}
}//5、建立 k 个元素的最大堆 O(n*log2k) 不会影响到原数组a[]
void Sift_max(int a[],int i,int n)
{
int j=2*i+1;
while(j<n)
{
//j指向较小的
if(j+1<n && a[j]<a[j+1])
j++;
if(a[i]>a[j])
break;
else
{
swap(a[i],a[j]);
i=j;
j=2*i+1;
}
}
}
void Heap_k( int a[],int b[],int n,int k)
{
//继续遍历数列,每次遍历一个元素 x,与堆顶元素比较,x<kmax,更新堆(用时 logk),否则不更新堆
for(int i=k;i<n;i++)
{
if(b[0]>a[i])
{
b[0]=a[i];
Sift_max(b,0,k);
}
}
//最终的k个元素不一定是有序的,若要最终的b[]中的k个元素从小到大排序,用排序算法再排即可
}//6、如果N个数都是正数,取值范围不太大,可以考虑用空间换时间。//申请一个包括N中最大值的MAXN大小的数组count[MAXN],count[i]表示整数i在所有整数中的个数。这样只要扫描一遍数组,就可以得到第K大的元素。
void Search_k(int a[],int b[],int n,int k,int MAX) { int *sum=new int[MAX+1]; for(int i=0;i<=MAX;i++) { sum[i]=0; } //统计0-MAX中各个数出现的次数 for(int i=0;i<n;i++) { sum[a[i]]++; } //从0-MAX中从0开始选出k个数 int j=0;//用来计k个数 for(int i=0;i<MAX+1;i++) { if(j>=k) break; if(sum[i]>0)//只有出现的数才行 { for(int m=0;m<sum[i];m++)//出现次数sum[i],赋值sum[i]次 { if(j<k)//防止最后一个值赋值越界 b[j]=i; else return ;//赋值结束了 j++; } } } delete []sum; }//7、类似partition的快速排序
void QuickSort_k(int a[],int first,int last,int k)
{
if(first<last)
{
int i=first,j=last;
while(i!=j)
{
if(i<j)//抓大放小
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
swap(i,j);
j++;
}
else
j--;}
if(i>j)
{
if(a[i]<a[j])
{
swap(a[i],a[j]);
swap(i,j);
j--;
}
else
j++;}
}
int len_a=i-first+1;
int len_b=last-(i+1)+1;
if(len_a>=k)
QuickSort_k(a,first,i-1,k);
else
{
QuickSort_k(a,i+1,last,k-len_a);
}
}
}
void main()
{
const int n=200,k=5;
int *a=new int
;
srand((unsigned)time(0));
cout<<"输入的数据: "<<endl;
for(int i=0;i<n;i++)
{
a[i]=rand()%200;
cout<<a[i]<<' ';
}
cout<<endl<<"-------------------------------------------------------------------"<<endl;/***********************方法1**************************/
//Search(a,n,k);/***********************方法2**************************/
// QuickSort(a,0,n-1);/***********************方法3**************************/
//SelectSort(a,n,k);/***********************方法4**************************/
//HeapSort_k(a,n,k);
//cout<<endl<<"-----------------------------------------------------"<<endl;
//for(int i=n-1;i>=n-k;i--)
//{
//cout<<a[i]<<' ';
//}
/***********************方法5**************************/
//建立长度为k的大顶堆
int b[k]={0};
for(int i=0;i<k;i++)
{
b[i]=a[i];
}
for(int i=(k-1-1)/2;i>=0;i--)
{
Sift_max(b,i,k);
}
//遍历,比较,更新
Heap_k(a,b,n,k);for(int i=0;i<k;i++)
{
cout<<b[i]<<' ';
}cout<<endl<<"-----------------------------------------------------"<<endl;
/***********************方法6**************************/
/*Search_k(a,b,n, k,200);
for(int i=0;i<k;i++)
{
cout<<b[i]<<' ';
}*//***********************方法7**************************/
QuickSort_k(a,0,n-1,k);
cout<<endl<<"-----------------------------------------------------"<<endl;
for(int i=0;i<k;i++)
{
cout<<a[i]<<' ';
}
cout<<endl<<"-----------------------------------------------------"<<endl;delete []a;
system("pause");
}

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐