如何从一个无序的数组中求出第K大的数(为了简化讨论,假设数组中的数各不相同)。例如,对数组{5,12,7,2,9,3}来说,第三大的数是5,第五大的数是9。

1.暴力破解:

对数组进行排序,直接取出第K大的元素,时间复杂度为O(nlogn).

程序代码:

#include<cstdio> 
#include<algorithm>
using namespace std;
int main(){
const int n=6;
int a[6]={5,12,7,2,9,3};
sort(a,a+n);//对数组a进行排序
int b;
while(scanf("%d",&b)!=EOF){
printf("%d\n",a[b-1]);
}
return 0;
}

运行结果:

随机选择算法_#include

想要了解sort函数的可以点击以下链接:

​C语言sort函数​

 

虽然看起来以上的算法时间复杂度很好,但是还有更优的算法,随机选择算法:

2.随机选择算法:

随机选择算法的原理类似于随机快速排序算法。

快速排序参考:快速排序

当对A[left, right]执行一次randPartition函数之后,主元左側的元素个数就是确定的,且它们都小于主元。假设此时主元是A[p],那么A[p]就是A[left,right]中的第p-left+1大的数。不妨令M表示p-left+1,那么如果K==M成立,说明第K大的数就是主元A[p];如果K<M成立,就说明第K大的数在主元左侧,即 A[left…(p-1)]中的第K大,往左侧递归即可;如果K>M成立,则说明第K大的数在主元右侧,即A[(p+1)…right]中的第K-M大,往右侧递归即可。算法以left==right作为递归边界,返回 A[left]。由此可以写出随机选择算法的代码:

程序代码:

#include<cstdio> 
#include<algorithm>
#include<cmath>
using namespace std;
bool comp(int a,int b) {
return a>b;
}

//和快速排序的randPartition一样
//对区间[left,right]进行划分
int randPartition(int A[], int left, int right) {
//生成[left,right]内的随机数p
int p = (round(1.0*rand()/RAND_MAX*(right-left)+left));
swap(A[p],A[left]);
int temp = A[left]; //将主元(A[left])存放至临时变量temp
while(left<right) { //只要left和right不相遇
while(left<right&&A[right]>temp) right--; //比较主元和从右侧开始遍历的元素,元素大于主元则左移
A[left] = A[right]; //元素不大于主元,将此元素A[right]挪到A[left]
while(left<right&&A[left]<=temp) left++; //比较主元和从左侧开始遍历的元素,元素小于等于主元则右移
A[right] = A[left]; //元素大于主元,将此元素A[left] 挪到A[right]
}
A[left] = temp; //把temp放到left与right相遇的地方
return left; //返回相遇的下标
}

//随机选择算法,从A[left,right]中返回第K大的数
int randSelect(int A[], int left, int right, int K) {
if(left==right)
return A[left];//边界
int p = randPartition(A,left,right); //划分后的主元位置
int M = p-left+1; //A[p]是A[left,right]中第M大
if(K==M) return A[p]; //找到K大的数
if(K<M) { //第K大的数在主元左侧
return randSelect(A,left,p-1,K); //往主元左侧找第K大
} else { //第K大的数在主元右侧
return randSelect(A,p+1,right,K-M); //往主元右侧找第K-M大
}

}
int main(){
const int n=6;
int a[6]={5,12,7,2,9,3};
sort(a,a+n);//对数组a进行排序
int b;
while(scanf("%d",&b)!=EOF){
printf("%d\n",randSelect(a,0,5,b));
}
return 0;
}

运行结果:

随机选择算法_#include_02

 

虽然随机选择算法的最坏时间复杂度是O(n^2),但是其对任意输入的期望时间复杂度却是O(n),这意味着不存在一组特定的数据能使这个算法出现最坏情况,是个相当实用和出色的算法(详细证明可以参考算法导论)。