输入 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");
}
相关文章推荐
- 输入n个整数,找出其中最小的K个数。——冒泡排序实现方法
- 输入10个整数,将其中最小的数与第一个数对换,把最大的数和最后一个数对换,写三个函数:1.输入10个数,2.进行处理.3输出10个数
- 华为(9)输入n个整数,输出其中最小的k个
- (用指针方法处理)有n个整数,使前面各数顺序向后移m个位置,最后m个数变成最前面m个数。编写一个函数实现上述功能,在主函数中输入n个整数,并输出调整后的n个数
- 输入n个整数,输出其中最小的k个
- 输入4个整数,输出其中的最大值和最小值
- 题目:输入n个整数,输出其中最小的k个。
- 输入n(n<100)个数,找出其中最小的数,将它与最前面的数交换后输出这些数.输入数据有多组,每组占一行,每行的开始是一个整数n,表示这个测试实例的数值的个数,跟着就是n个整数。n=0表示输入的结束,
- //1、输入10个整数,将其中最小的数与第一个数对换,把最大的数和最后一个数对换,写三个函数:1.输入10个数,2.进行处理.3输出10个数
- 输入10个整数,将其中最小的数与第一个数对换,把最大的数与最后一个数对换。写三个函数; ①输入10个数;②进行处理;③输出10个数。
- 输入n个整数,输出其中最小的k个
- [Baidu面试题]题目1:输入n个整数,输出其中最小的k个。
- 输入一个整数,使用递归方法实现反向输出
- Java实现输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
- 输入n个整数,输出其中最小的k个
- 每天三道冲刺工作--输入n个整数,输出其中最小的k个。
- 【华为OJ】输入n个整数,输出其中最小的k个
- 排序,求几个最值问题,输入n个整数,输出其中最小的k个元素。
- 华为oj: 输入n个整数,输出其中最小的k个
- 华为OJ试题:输入n个整数,输出其中最小的k个