关于k小的实现及优化(c)

在分治中有一个经典案例是求中项及第k小的元素。先不管算法书里面是怎么解决这个问题的,如果让我们自己来想,这个问题应该怎么解决?惯性思维肯定是先排序然后求出第k小的值。
排序算法我们首推肯定是快速排序,用快速排序来求第k小的值的算法如下(c):

#include "stdafx.h"

void quickSort(int a[],int s,int k){
    int i,j,tmp;
    if(s<k){
        i = s;j=k,tmp = a[s];
        while(i<j){
            while(i<j&&tmp<=a[j])
                j--;
            a[i] = a[j];
            while(i<j&&a[i]<=tmp)
                i++;
            a[j] = a[i];
        }
        a[i] = tmp;
        quickSort(a,s,i-1);
        quickSort(a,i+1,k);
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    int a[] = {8,7,6,5,4,3,2,1},k;
    quickSort(a,0,7);
    for(int  i  = 0;i<8;i++)
        printf("%d",a[i]);
    printf("\n");
    scanf("%d",&k);
    printf("k小:%d\n",a[k-1]);
    system("pause");
    return 0;
}

更进一步,我们发现其实如果单纯的求k小并不需要把整个数组都排好序。当一趟划分完之后,如果i小于k,那么k小在后半部分,前半部分就不需要递归排序了;同理,如果i大于k,那么k小在前半部分,后半部分就不需要递归排序了。
修改后的算法如下:

#include "stdafx.h"

void quickSort(int a[],int s,int k,int x){
    int i,j,tmp;
    if(s<k){
        i = s;j=k,tmp = a[s];
        while(i<j){
            while(i<j&&tmp<=a[j])
                j--;
            a[i] = a[j];
            while(i<j&&a[i]<=tmp)
                i++;
            a[j] = a[i];
        }
        a[i] = tmp;
        if(i>x-1){
            quickSort(a,s,i-1,x);
        }else if(i<x-1){
            quickSort(a,i+1,k,x);
        }
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    int a[] = {8,7,6,5,4,3,2,1},k;
    scanf("%d",&k);
    quickSort(a,0,7,k);
    printf("k小:%d\n",a[k-1]);
    system("pause");
    return 0;
}

很明显,第二种写法比第一种简单了许多。上述两种方法的时间复杂度均为 nlogn。接下来看看算法书上的方法,与上述的思想类似,也是从递归调用的途中舍弃掉一部分无关的值,不过比上述方法要更加复杂一些:

void quickSort2(int a[],int s,int k){
    int i,j,tmp;
    if(s<k){
        i = s;j=k,tmp = a[s];
        while(i<j){
            while(i<j&&tmp<=a[j])
                j--;
            a[i] = a[j];
            while(i<j&&a[i]<=tmp)
                i++;
            a[j] = a[i];
        }
        a[i] = tmp;
        quickSort2(a,s,i-1);
        quickSort2(a,i+1,k);
    }
}
//寻找第k小的元素,但会破坏原数组的顺序  
int select(int * A, int low, int high, int k) {  
    int result = 0;  
    int p = high-low+1;  
    if (p < 6/*44*/) {  
        quickSort2(A, low, high);  
        return A[k-1];  
    }  
    int q = p / 5;  
    int * M = new int [q]; 
    for (int i = 0; i < q; i++) {  
        quickSort2(A, i*5, i*5+4);  
        M[i] = A[i*5+2];  
    }  
    int mm = select(M, 0, q-1, int(ceil(q/2.0)));  

    int * A1 = new int [p];  
    int * A2 = new int [p];  
    int * A3 = new int [p];  
    int count1 = 0, count2 = 0, count3 = 0;  
    for (int i = low; i <= high; i++) {  
        if (A[i] < mm) {  
            A1[count1++] = A[i];  
        } else if (A[i] == mm) {  
            A2[count2++] = A[i];  
        } else {  
            A3[count3++] = A[i];  
        }  
    }  
    if (count1 >= k) {  
        result = select(A1, 0, count1-1, k);  
    } else if (count1+count2 >= k) {  
        result = mm;  
    } else if (count1+count2 < k) {  
        result = select(A3, 0, count3-1, k-count1-count2);  
    }  
    return result;  
}  

A中总共q个数,分成q/5组,每组5个元素。每组分别取中项,组成一个大小为q/5的数组M,求M的中项mm(若M中元素数大于6则一直递归,否则通过快排求出mm)。将A分i成三份A1,A2,A3。分三种情况讨论:若k小在A1,将A2,A3舍弃,A=A1;若k小在A2,则直接输出k=mm;若k小在A3,将A1,A2舍弃,A=A3。继续递归,一直到求出k小为止。
这种算法的时间复杂度可以达到 n。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值