问题描述
有 n 个正整数,要求出这 n 个正整数中的第 k 个最小整数。
思路一:桶排序
//桶排序
void tong(int n, int k, int *a, int *res){
for(int i=1;i<=n;i++){
res[a[i]]++;
}
int ct=0;
for(int i=1;i<30010;i++){
if(res[i]){
ct++;
}
if(ct==k){
cout<<i;
break;
}
}
}
桶排序实现起来比较简单。
思路二:分治法
//分治法
//left:快排的最左侧下标
//right:快排的最右侧下标
//n:数组内数据总数
//k:目标要找的第k大
//a:数组
void qs(int left ,int right, int n, int k, int *a){
//m:作为参照的中间值的下标
//middle:作为参照的中间值
int l=left, r=right, m=(l+r)/2;
int middle=a[m];
int i=l, j=r;
while(i<=j){
if(a[i]>middle)
i++;
if(a[j]<middle)
j++;
if(i<=j){
int tmp=a[i];
a[i]=a[j];
a[j]=tmp;
//当中间值换了位置 要换m下标值
if(i==m)
m=j;
if(j==m)
m=i;
}
}
if(m==k)//说明第k小数就是middle
cout>>middle;
if(m>k)//说明第k小数在左边
f2(1, k-1, k-1, k, a);
if(m<k)//说明第k小数在右边
f2(K+1, n, n-k, k, a);
}
主要利用部分快排的思想,选一个参照值(我这里每次取的是位于数组中间的那个值),进行一次快排,将数组大致分为
(比参照值小的,参照值,比参照值大的)
这三组。
然后按照m与k的大小关系分类,m>k时显然要找的数在middle值的左边,所以接下来只用讨论middle值左边的那组数据;m<k时显然就只用讨论middle值右边的那组数据。
需要注意的是需要记录middle值的下标,因为当middle值与其他值进行交换操作时,它的下标也应该换成交换后所处位置的下标。比如:
初始时中间值middle为20,下标是5(数组从1开始存放)。经过一次快排后20被放到了最后,m应变为9。