题目
给定一个数组,输出第k小的数。
思路
可以借用快速排序的思想。我们知道,快速排序的核心思想就是在要排序的数组中选择一个数,然后将数组中比这个数小的数放在这个数的左边。比这个数大的数放在他的右边。现在我们要找第k大的数,那么如果在数组中选了一个数,用快排的方法,将比这个数小的数都放在它的左边,而左边的这些数正好有k-1个,那我们选的这个数不就是第k大的数?
代码
#include <iostream>
using namespace std;
const int MAXN = 100;
int a[MAXN];
int quick_select(int a[],int l,int r,int k){
//生成一个[l,r]范围内的随机数
int p =rand()%(r-l+1)+l;
int x = a[p];
//将毛点移到最右边
int t = a[p];
a[p] = a[r];
a[r] = t;
int i = l,j = r;
while(i < j){
//在左边找一个比x大的数
while(i < j && a[i] < x){
i++;
}
//将左边的比x大的数放到右边
if(i < j){
a[j] = a[i];
j--;
}
//在右边找一个比x小的数
while(i < j && a[j] > x){
j--;
}
//将右边的比x小的数放到左边
if(i < j){
a[i] = a[j];
i++;
}
}
//将选择的数放中间
a[i] = x;
int leftNumber = i-l;
//如果左边有k-1个数,那么这个数就是第k大的数
if(leftNumber == k-1){
return a[i];
}else if (leftNumber > k-1){
//如果左边的数大于k-1个,说明我们选的数偏大了,需要向左靠近
return quick_select(a,l,i-1,k);
}else {
//如果左边的数小于k-1个,说明我们选的数偏小了,需要向右靠近
//为了使问题简化,由于我们选的a[i]的左边已经排好了序(包括a[i])
//那么我们只需要在右边(即[i+1,r]这个范围内)再找出第k-i大的数就行了
return quick_select(a,i+1,r,k-leftNumber-1);
}
}
int main()
{
int n,k;
scanf("%d",&n);
for(int i =0;i<n;i++){
scanf("%d",&a[i]);
}
scanf("%d",&k);
printf("%d\n",quick_select(a,0,n-1,k));
return 0;
}