【十大排序算法】基数排序

数字犹如无数繁星,基数排序如晨曦的指引,将混沌序列织就成和谐的序曲。

一、基数排序

基数排序是一种非比较性的排序算法,它根据元素的位数来对元素进行排序。基数排序通常适用于元素为整数或字符串的情况。算法的基本思想是将待排序的元素按照个位、十位、百位等位置上的数字进行分桶,然后依次对每个位置上的数字进行排序,最终得到排好序的结果。

基数排序的具体步骤如下:

  1. 确定待排序元素的最高位数,即最大位数。
  2. 从最低位开始,依次对元素按照位数进行分桶。
  3. 对每个位数上的数字进行排序,可以使用稳定的排序算法,如计数排序或桶排序。
  4. 重复上述步骤,直到所有位数上的数字都被排序完毕。

二、发展历史

基数排序是一种经典的排序算法,它的发展历史可以追溯到 19 世纪的普兰德尔(Herman Hollerith)发明的用于制表机的卡片排序技术。然而,真正的基数排序算法在计算机科学领域的发展可以追溯到 20 世纪的早期。

  1. 普兰德尔的卡片排序技术(1880 年代):在普兰德尔的制表机中,卡片按照特定的字段(例如人口普查中的姓名、年龄等)进行排序。这可以被看作是基数排序的早期形式。
  2. Herman Hollerith的制表机(1890 年):Hollerith 设计了一种用于美国人口普查的制表机,该机器可以根据人口普查的数据对卡片进行排序和统计。
  3. Radix Exchange排序(1930 年代):Radix Exchange 排序是基数排序的一种形式,它最早由 Harold H. Seward 在 20 世纪 30 年代提出。这个算法的基本思想是将待排序的元素按照高位到低位或者低位到高位的顺序进行多次排序,直到整个序列有序。
  4. 桶排序(1950 年代):桶排序是一种基数排序的变体,最早由 Arnold Schönhage 和 Peter van Emde Boas 在 1957 年提出。它的基本思想是将待排序的元素划分到有限数量的桶中,然后对每个桶中的元素进行排序。
  5. 基数排序(1960 年代):真正意义上的基数排序算法在 1960 年代开始出现。这种算法的基本思想是将待排序的元素按照其数位上的值进行分组,然后依次对各个数位上的值进行排序,直到整个序列有序。

三、处理流程

场景假设:我们需要将下面的无序数组使用基数排序按从小到大进行排序。
workspace (26).png

  1. 第一轮(个位数):我们先按照个位数进行排序:

workspace (27).png

  1. 第二轮(十位数):然后,我们按照十位数进行排序:

workspace (28).png

  1. 第三轮(百位数):最后,我们按照百位数进行排序:

workspace (29).png

四、算法实现

public class RadixSort {
    // 主函数,用于执行基数排序
    public static void radixsort(int arr[]) {
        // 找到数组中的最大值
        int max = Arrays.stream(arr).max().getAsInt();
        // 从最低有效位开始(exp = 1),对数组进行排序
        for (int exp = 1; max / exp > 0; exp *= 10)
        countSort(arr, exp);
    }

    // 计数排序函数,用于对指定位数进行排序
    private static void countSort(int arr[], int exp) {
        // 创建输出数组
        int output[] = new int[arr.length];
        // 创建计数数组,用于存储每个位数的数量
        int count[] = new int[10];
        // 初始化计数数组
        Arrays.fill(count, 0);

        // 计算每个位数的数量
        for (int i = 0; i < arr.length; i++)
        count[(arr[i] / exp) % 10]++;

        // 计算计数数组的累计和,这将用于确定输出数组的索引
        for (int i = 1; i < 10; i++)
        count[i] += count[i - 1];

        // 使用计数数组和输入数组,生成输出数组
        for (int i = arr.length - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }

        // 将输出数组复制回输入数组,以便下一轮排序
        for (int i = 0; i < arr.length; i++)
        arr[i] = output[i];
    }

    // 测试基数排序函数
    public static void main(String args[]) {
        int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
        radixsort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

算法时间复杂度分析:

情况时间复杂度计算公式公式解释
最好情况 O ( n k ) O(nk) O(nk) T ( n ) = n ∗ k T(n) = n * k T(n)=nk n n n 是要排序的元素数量, k k k 是数字的最大位数
平均情况 O ( n k ) O(nk) O(nk) T ( n ) = n ∗ k T(n) = n * k T(n)=nk n n n 是要排序的元素数量, k k k 是数字的最大位数
最坏情况 O ( n k ) O(nk) O(nk) T ( n ) = n ∗ k T(n) = n * k T(n)=nk n n n 是要排序的元素数量, k k k 是数字的最大位数

五、算法特性

基数排序具有以下几个主要特性:

  1. 稳定性:基数排序是一种稳定的排序算法,即对于具有相同关键字的元素,它们在排序后的相对位置不会改变。这是因为基数排序是根据键值的每个数位进行排序的,而且是从低位到高位或从高位到低位依次进行的,这样可以保持具有相同数位值的元素的相对顺序不变。
  2. 原地性:基数排序不是原地排序算法,因为在排序过程中,需要额外的空间来存储临时的排序结果。
  3. 适用性:基数排序适用于一定范围内的整数或字符串等具有确定位数的数据集合。它的效率主要取决于数据的位数和基数(进制),而不是数据集合的大小。因此,对于位数较少但数据范围较大的数据集合,基数排序通常比较高效。
  4. 非比较性:与快速排序、归并排序等基于比较的排序算法不同,基数排序是一种非比较性的排序算法。它不涉及元素之间的比较操作,而是根据键值的每个数位进行桶分配和收集,然后根据桶中元素的顺序重新组合序列。因此,基数排序的时间复杂度不受元素之间比较操作的影响。

六、小结

基数排序作为一种非比较性的稳定排序算法,在处理特定范围内的数据集合时具有较高的效率和性能。通过按照数位的顺序依次对元素进行排序,基数排序能够在保持稳定性的同时,实现对数据的快速排序。在实际应用中,基数排序常常被用于处理整数、字符串等具有确定位数的数据集合,为解决排序问题提供了一种简单而有效的解决方案。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await
  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值