寻找最大的K个数
Posted xuguoli_beyondboy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了寻找最大的K个数相关的知识,希望对你有一定的参考价值。
编程之美有一道考察多种排序的题目,题目如下:
有一个长度为N的无序数组,假定其中的每一个元素都各不相等,求其中最大的K个数。
作者对于此题目结合各种排序算法给出了五种解法思路。
解法一:
使用快速排序或堆排序对它们元素进行排序,整个排序的时间复杂度为O(N*
log2
N),然后取出前K个,时间复杂度为O(K),总时间复杂度O(N*
log2
N)+O(K)=O(N*
log2
N).根据题目要求,并不需要后N-KN-K个数有序,而只需要寻找最大K的个数,因此可以用选择排序和交换排序进行部分排序来筛选最大的K个数,其时间复杂度为O(N*K).
解法二:
利用快速排序思想对数据进行分组,让其一组数据的任何一个数都比另一组中的任意数大,整个算法的时间复杂度是O(N*
log2
K),其情况如下:
假设N个数存储在数组S中,我们从数组S中随机的找到一个元素X,把数组分成两部分Sa和Sb。Sa中的元素大于等于X,Sb中的元素小于X。
(1)Sa中的元素个数小于K,Sa中所有的数和Sb中最大的K-|Sa|个元素就是数组S中最大的K个数。
(2)Sa中的元素的个数大于等于K,则需要返回Sa中最大的K个元素。
代码实现:
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
//返回基准索引
int partition(int a[],int low,int height)
//存储基准值对应数组的索引
int index;
//利用随机选择比较基准值,以避免特殊数据下的算法退化
swap(a[low],a[low+rand()%(height-low+1)]);
int p=a[low];//比较的基准值
int i=low,j=height;
do
//从右端开始寻找大于基准值的第一个值
while(i<j&&a[j]<p)j--;
//比基准值大的数往左边移
if(i<j)
a[i++]=a[j];
//从左端端开始寻找小于基准值的第一个值
while(i<j&&a[i]>=p)i++;
//比基准值小的数往右边移
if(i<j)
a[j--]=a[i];
while(i<j);
a[i]=p;
index=i;
return index;
int Kbig(int a[],int low,int height,int k)
if(low>=height)
return a[low];
int index=partition(a,low,height);
//获得数组连续最大的数目
int count=index-low+1;
//获得数组连续最大的数目等于K则输出
// 若小于K则是第一种情况
//若大于K则是第二种情况
if(count==k)
return a[index];
if(count<k)
return Kbig(a,index+1,height,k-count);//后面部分寻找最大的K个数
else
return Kbig(a,low,index-1,k);//前面部分寻找最大的K个数
//交换数据
void swap(int *a,int *b)
int temp=*a;
*a=*b;
*b=temp;
int main()
int a[]=100,2000,45,68,5,700,9,50,45,89,87;
int len=sizeof(a)/sizeof(int);
int k=5;
cout<<Kbig(a,0,len-1,k)<<endl;
for(int i=0;i<k;i++)
cout<<a[i]<<"\\t";
return 0;
解法三思路:
寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,也就是第K大的数,因此可以使用二分搜索的策略,对一个给定的数p,可以在O(N)的时间复杂度内找出所有不小于P的数,假如N个常数最大的数为Vmax,最小的数为Vmin,那么这N个数中的第K大数一定在区间[Vmin,Vmax]之间,整个算法的时间复杂度也为O(N*
log2
N).
代码实现:
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
int find(int*a,int x,int len)
int number=0;
for(int i=0;i<len;i++)
if(a[i]>=x)
number++;
return number;
int findKMax(int *a,int max,int min,int k,int len,int delta)
while(max-min>delta)
int mid=min+(max-min)/2;
//若后面部分大于mid的个数大于K,那么查找后半部分
if(find(a,mid,len)>=k)
min=mid;
//若后面部分大于mid的个数小于K,那么查找前半部分
else
max=mid;
return min;
int main()
int a[]=100,2000,45,68,5,700,9,50,45,89,87;
int len=sizeof(a)/sizeof(int);
int k=5;
//该取值要比所有N个数中的任意两个不相等的元素差值之最小值都要小
int delta=1;
cout<<findKMax(a,2000,5,5,len,delta)<<endl;
return 0;
解法四思路
如果数据很大,100亿多个,这个时候数据不能全部装入内存,所以要求尽可能少地遍历所有数据,如果能通过逐渐的加入元素来判断,能就可以解决这问题了,因此可以用最小堆的结构来求出前面已加入元素的前K个数,因此可以构造K个元素的小顶堆来存储最大的K个数,最小堆的堆顶(Y)就是最大的K个数中的最小的一个,对于每一个新加入的数X,如果X < Y,则不需要更新堆,如果X>Y,那么用X替换掉堆顶的元素Y,然后调整小顶堆结构,整个过程遍历一次数组元素就行了,因此整个算法的时间复杂度O(N *
log2
K ).
代码实现:
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
//调整堆
void adjustMinHeap(int *a,int m,int n)
int i=m;
int j=2*i+1;//左结点
int temp;//临时存储
while(j<n)
//比较左右结点得到较小的那个结点
if(j+1<n&&a[j]>a[j+1])
j++;
//若父结点大于较小的那个子结点,则交换,否则就跳出循环
if(a[i]>a[j])
temp=a[j];
a[j]=a[i];
a[i]=temp;
i=j;
j=2*i+1;
else
break;
//创建k个元素最小堆
void createMinHead(int *a,int k)
//自下而上的建立堆,从最后一个非叶子结点开始初始化最小堆
for(int i=k/2-1;i>=0;i--)
adjustMinHeap(a,i,k);
void FindKMax(int *a,int n,int *kMax,int k)
for(int i=0;i<k;i++)
kMax[i]=a[i];
//初始K个元素最小堆
createMinHead(kMax,k);
for(int i=k;i<n;i++)
//如果该元素大于她的最小堆则替换
if(a[i]>kMax[0])
kMax[0]=a[i];
adjustMinHeap(kMax,0,k);
int main()
int a[]=100,2000,45,68,5,700,9,50,45,89,87;
int len=sizeof(a)/sizeof(int);
int k=5;
int * kMax=(int*)malloc(k*sizeof(int));
FindKMax(a,len,kMax,k);
for(int i=0;i<k;i++)
cout<<kMax[i]<<"\\t";
return 0;
第五中解法思路:
利用计数排序思想,如果所有N个数都是正整数,且它们的取值范围不太大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个,比如,所有整数都在(0,MAXN)区间中的话,利用一个数组count[MAXN]来记录每个整数出现的个数(count[i]表示整数i在所有整数中出现的个数),我们只须扫描一遍就可以得到count数组,然后寻找第K大的元素,整个算法时间复杂度为O(n).
代码实现:
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
#define MAX 100000
int FindKMax(int *a,int k)
int sumCount=0;
int i;
//从大到小开始计算其元素个数,直到为K
for(i=MAX-1;i>=0;i--)
sumCount+=a[i];
if(sumCount>=k)
break;
return i;
int main()
int a[]=100,2000,45,68,5,700,9,50,45,89,87;
int len=sizeof(a)/sizeof(int);
int count[MAX]=0;
int k=5;
//统计a元素对应出现个数
for(int i=0;i<len;i++)
count[a[i]]++;
cout<<FindKMax(count,k)<<endl;
return 0;
以上是关于寻找最大的K个数的主要内容,如果未能解决你的问题,请参考以下文章