排序——基数排序(Radix sort)

12 篇文章 23 订阅

概述

基数排序(Radix sort)属于“分配式排序”(distribution sort),它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用。

基数排序方法有两类:按照位数比较顺序的不同将其分为 LSD(Least significant digital) MSD(Most significant digital)两类。

  1. 最高位优先(Most Significant Digit First)法,简称MSD法:先按 k1 排序分组,同一组中记录,关键码 k1 相等,再对各组按 k2 排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码 kd 对各子组排序后。再将各组连接起来,便得到一个有序序列。
  2. 最低位优先(Last Significant Digit First)法,简称 LSD 法:先从 kd 开始排序,再对 kd-1 进行排序,依次重复,直到对 k1 排序后便得到一个有序序列。

下图我们针对 LSD 法进行讲解。

算法思路

基数排序的思想就是先排好个位,然后排好个位的基础上排十位,以此类推,直到遍历最高位次,排序结束。基数排序不是比较排序,而是通过分配和收集的过程来实现排序。初始化 10 个固定的桶,桶下标为 0-9。

1、将所有待比较数据统一为相同位数长度;

2、按照个位数进行排序;

3、按照十位数进行排序;

4、以此类推,已知到最高位进行排序。这样就得到一个有序序列。

图解算法

假设有一个数列 {53, 542, 3, 63, 14, 214, 154, 748, 616} 进行排序的过程如下图所示。

动画展示

算法性能

时间复杂度

初看起来,基数排序的执行效率似乎好的让人无法相信,所有要做的只是把原始数据项从数组复制到链表,然后再复制回去。如果有 10 个数据项,则有 20 次复制,对每一位重复一次这个过程。假设对 5 位的数字排序,就需要 20*5=100 次复制。如果有100 个数据项,那么就有 200*5=1000 次复制。复制的次数与数据项的个数成正比,即O(n)。这是我们看到的效率最高的排序算法。

不幸的是,数据项越多,就需要更长的关键字,如果数据项增加 10  倍,那么关键字必须增加一位(多一轮排序)。复制的次数和数据项的个数与关键字长度成正比,可以认为关键字长度是 N 的对数。

平均、最好、最坏都为O(k*n),其中 k 为常数,n 为元素个数。

空间复杂度

O(n + k)

稳定性

稳定

代码实现

C和C++

/*
*求数据的最大位数,决定排序次数
*/
int maxbit(int data[], int n) {
    int d = 1; //保存最大的位数
    int p = 10;
    for(int i = 0; i < n; ++i) {
        while(data[i] >= p) {
            p *= 10;
            ++d;
        }
    }
    return d;
}

void radixSort(int data[], int n) {
    int d = maxbit(data, n);
    int tmp[n];
    int count[10]; //计数器
    int i, j, k;
    int radix = 1;
    for(i = 1; i <= d; i++)  {
        //进行d次排序
        for(j = 0; j < 10; j++) {
            count[j] = 0; //每次分配前清空计数器
        }
        for(j = 0; j < n; j++) {
            k = (data[j] / radix) % 10; //统计每个桶中的记录数
            count[k]++;
        }
        for(j = 1; j < 10; j++) {
            count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
        }
        for(j = n - 1; j >= 0; j--) {
            //将所有桶中记录依次收集到tmp中
            k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        for(j = 0; j < n; j++) {
            //将临时数组的内容复制到data中
            data[j] = tmp[j];
        }
        radix = radix * 10;
    }
}

Java

/**
 * 基数排序
 * 考虑负数的情况还可以参考: https://code.i-harness.com/zh-CN/q/e98fa9
 */
public class RadixSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int maxDigit = getMaxDigit(arr);
        return radixSort(arr, maxDigit);
    }

    /**
     * 获取最高位数
     */
    private int getMaxDigit(int[] arr) {
        int maxValue = getMaxValue(arr);
        return getNumLenght(maxValue);
    }

    private int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

    protected int getNumLenght(long num) {
        if (num == 0) {
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }

    private int[] radixSort(int[] arr, int maxDigit) {
        int mod = 10;
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
                for (int value : bucket) {
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int[] arrayAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }
}

 

 

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值