05-线性时间排序

1.计数排序

计数排序的基本思想是:确定数组a输入的元素都小于x,然后辅助数组c的长度为x。把数组中的元素对应辅助数组c的下标。

a是输入数组,b是输出数组,c是临时辅助数组,k要大于数组a中最大元素。

public static void countSort(int[] a, int[] b, int k){
    int[] c = new int[k];
    for (int i = 0; i < k; i++) {
        c[i] = 0;
    }
    for (int i = 0; i < a.length; i++) {
        c[a[i]] = c[a[i]] + 1;
    }
    for (int i = 1; i < k; i++) {
        c[i] = c[i] + c[i-1];
    }
    for (int i = a.length-1; i >= 0; i--) {
        b[c[a[i]] -1] = a[i];
        c[a[i]] = c[a[i]] -1;
    }
}

最后一个循环中,倒序遍历a数组的原因是:相同元素的在输出数组中的相对次序与它们在输入数组中的相对次序是一致的。计数排序一个重要的性质是稳定

代码中涉及四个循环,第一个所花的时间为\Theta(k),第二个所花的时间为\Theta(n),第三个所花的时间为\Theta(k),第四个所花的时间为\Theta(n)。所以计数排序的时间复杂度为\Theta(k+n)。

2.基数排序

基数排序引用了计数排序,假设n个d位数的元素存放在数组中,其中l是最低位,d是最高位,从l到d依次对每个位数进行计数排序。

/**
 * 基数排序
 */
public static int[] radixSort(int[] a){
    //遍历最大数
    int max = a[0];
    for (int i = 1; i < a.length; i++) {
        max = a[i] > max ?  a[i] : max;
    }
    //查看最大的数几位
    int d = 0;
    while (max > 0){
        max /= 10;
        d++;
    }
    for (int i = 0; i < d; i++) {
        a = countSort(a, (int)Math.pow(10,i));
    }
    return a;
}

/**
 * 计数排序
 */
public static int[] countSort(int[] a, int dividend){
    int k = 10;
    int[] c = new int[k];
    int[] b = new int [a.length];
    for (int i = 0; i < k; i++) {
        c[i] = 0;
    }
    for (int i = 0; i < a.length; i++) {
        int digit = (a[i]/dividend)%10;
        c[digit] = c[digit] + 1;
    }
    for (int i = 1; i < k; i++) {
        c[i] = c[i] + c[i-1];
    }
    for (int i = a.length-1; i >= 0; i--) {
        int digit = (a[i]/dividend)%10;
        b[c[digit] -1] = a[i];
        c[digit] = c[digit] -1;
    }
    return b;
}

给定n个d位数,其中每一位有k个可能的取值,如果每个位数的排序耗时O(n+k),d位数,则耗时O(d(n+k))。

同理也可得出给定一个b位数和任何正整数r<=b,使用稳定的排序算法对数据取值区间为0-k的输入进行排序耗时O(n+k),那么就可以在O(\frac{b}{r}*(n+2^{r}))内排好序。

例如可以将一个32位的字看成4个8位的数,于是又b=32,r=8,k=2^{r}-1 ,d= \frac{b}{r} = 4。

对于给定的b和n,我们希望所选择的r值能够最小化表达式 (b/r)(n+2^{r}),如果b<[lgn],则r<[lgn]\Rightarrow2^{r}<n,所以选择r=b时,时间代价为(b/b)(n+2^{r}2^{b}) = \Theta(n)。如果b>=[lgn],选择r=[lgn],得到的运行时间为\Theta(bn/lgn)。随着r值的增加至大于[lgn],得到到时间代价为\Omega(bn/lgn)。

基数排序和基于比较的比较排序那种更好,通常情况如果b=O(lgn),我们选择r\approxlgn,则基数的排序时间为\Theta(n)。这一结果看上去要比快速排序的期望运行时间\Theta(nlgn)更好一些。但是隐含在\Theta里的常数因子是不同的。在处理n个关键字时,基数排序的循环次数要比快速排序要少,但每一轮耗费的时间要长的多。哪一种排序算法更适合要取决于具体实现和底层硬件的特性(例如快速排序通常比基数排序更有效的使用硬件的缓存),以及输入的特征。此外,利用计数排序作为中间稳定排序的基数排序不是原址排序,而很多\Theta(nlgn)时间的比较排序都是原址排序。因此当主存的容量比较宝贵时,我们可能会倾向于快速排序这样的原址排序。

3.桶排序

基本思想:先用一个数组当成很多桶,然后n个数能均匀的放在各个桶中,然后同种类型的数据落在一个桶中时,用链表存放并排序。

时间复杂度为\Theta(n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值