天天看点

第k小整数问题

问题描述

有 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值与其他值进行交换操作时,它的下标也应该换成交换后所处位置的下标。比如:

第k小整数问题

初始时中间值middle为20,下标是5(数组从1开始存放)。经过一次快排后20被放到了最后,m应变为9。

继续阅读