标签:堆排序 计数排序 快速排序 二分查找 寻找数组k个元素
编程之美有一道考察多种排序的题目,题目如下:
有一个长度为N的无序数组,假定其中的每一个元素都各不相等,求其中最大的K个数。
作者对于此题目结合各种排序算法给出了五种解法思路。
解法一:
使用快速排序或堆排序对它们元素进行排序,整个排序的时间复杂度为O(N*
解法二:
利用快速排序思想对数据进行分组,让其一组数据的任何一个数都比另一组中的任意数大,整个算法的时间复杂度是O(N*
假设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*
代码实现:
#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 *
代码实现:
#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个元素
原文地址:http://blog.csdn.net/xuguoli_beyondboy/article/details/48162405