基数排序详解(Radix sort)

简介: 基数排序详解(Radix sort)

一、简单释义

1、算法概念

冒泡排序(Radix sort)是属于“分配式排序”(distribution sort),又称“桶排序”(bucket sort),顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

2、算法目的

 将原本乱序的数组变成有序,可以是 LSD或者 MSD  (文中以LSD为例)

3、算法思想

 将整数按位数切割成不同的数字,然后按每个位数分别比较。基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

4、算法由来

 这个算法的名字由来是因为每个数按照它的位数进行拆分,对每一个对应的位数进行比较排序,直到所有位数都进行过一遍排序位置

二、核心思想

「迭代」:类似的事情,不停地做。

「哈希」:将一个数字映射到一个数组中。

「队列」:采用先进先出的数据结构。

三、图形展示

1、宏观展示

ebebe72d478047629168a76aa2eb2612.png

2、微观展示

 以324、61、4、10、222、166、512这个数组为例

9e2262baf13c43278ad2dd4dd6a9d599.png

 1、第一次排序:按照每个元素个位数的值填入到对应的桶中,324的个位数是4所以放到4号桶中。从桶向外取值形成新的数组是以队列的先进先出的方式进行的。

cdd2d91b1972407992bb7d8a8f67592d.png

 2、第二次排序:按照每个元素十位数的值填入到对应的桶中,十位上没有数的元素默认填入到0号桶中即可。其余操作和第一次排序是一样的。比如512十位数的值是1,那么把512这个元素放到1号桶中。从1号桶中取值的时候先取10这个元素,在取512这个元素。

a9cf9e84f6cf4ecfa3912a1502af52fb.png

 3、第三次排序:按照每个元素百位数的值填入到对应的桶中,百位上没有数的元素默认填入到0号桶中即可。其余操作和上面两次排序是一样的。数组中元素的最高位数就是基数排序要进行的排序的次数。

四、算法实现

1、实现思路

 将图形化展示的过程转换成对应的开发语言,本文中主要以Java语言为例来进行算法的实现。

 1. 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零;

 2. 从最低位开始,依次进行一次排序;

 3. 这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列;

 4. 重复步骤1~3,直到排序完成。

 能把整个过程描述清楚实现起来才会更加容易!!!

2、代码实现

import java.util.Arrays;
/**
 * @BelongsProject: demo
 * @BelongsPackage: com.wzl.Algorithm.RadixSort
 * @Author: Wuzilong
 * @Description: 描述什么人干什么事儿
 * @CreateTime: 2023-05-06 11:24
 * @Version: 1.0
 */
public class RadixSort {
    public static void main(String[] args) {
        int[] numArray={324,61,4,10,222,166,512};
        radixSort(numArray);
    }
    public static void radixSort(int[] arr) {
        // 获取数组中最大的数,确定最高位数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        int maxLength = String.valueOf(max).length();
        // 定义一个桶,存储每个位数上的数字
        int[][] bucket = new int[10][arr.length];
        // 定义一个数组,记录每个桶中存储的数据个数
        int[] bucketCount = new int[10];
        // 按照从低位到高位的顺序进行排序
        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
            // 将每个数的第 i+1 位数存到桶中
            for (int j = 0; j < arr.length; j++) {
                int digit = arr[j] / n % 10;
                bucket[digit][bucketCount[digit]++] = arr[j];
            }
            // 将桶中的数字重新放回到原数组中,这里要注意桶的顺序
            int index = 0;
            for (int j = 0; j < bucket.length; j++) {
                if (bucketCount[j] != 0) {
                    for (int k = 0; k < bucketCount[j]; k++) {
                        arr[index++] = bucket[j][k];
                    }
                    bucketCount[j] = 0;
                }
            }
            // 打印每次排序的结果
            System.out.println("第"+(i+1)+"次"+Arrays.toString(arr));
        }
    }
}

3、运行结果

e58618e66fed48548b52768adcf1adaa.png

五、算法描述

1、问题描述

 给定一个 n 个元素的整型数组,数组下标从 0开始,采用基数排序,将数组按照 「LSD」排列。

2、算法过程

整个算法过程分为以下几步:

 1) n个记录的关键字进行排序,每个关键字看成是一个d元组:ki=(ki1, ki2,…, kid) 其中c0 <=kij <=cr-1 ( 1 <=i <=n, 1 <=j <=d )

 2) 排序时先按kid 的值,从小到大将记录分到r(称为基数)个盒子中,再依次收集;

 3)然后按kid-1的值再这样作。直至按ki1分配和收集序列为止,排序结束。

3、算法总结

 首先,准备 10 个队列,进行若干次迭代 。每次迭代 ,先清空队列,然后取每个待排序数的对应十进制位,通过 哈希 ,映射到它对应的队列 中,然后将所有数字按照队列顺序 塞回 原数组 完成一次 迭代 。

 可以认为类似 关键字排序 ,先对 第一关键字 进行排序,再对第二关键字 排序,以此类推,直到所有关键字都有序为止。

六、算法分析

1、时间复杂度

 基数排序的时间复杂度为O(d*(n+k)),其中d为最大数的位数,n为待排序的数据个数,k为桶的个数。在数据分布均匀的情况下,基数排序的效率很高,但如果数据分布极端不均匀,则可能导致效率下降,因此在实际使用中需要根据具体情况进行选择。

2、空间复杂度

 基数排序的空间复杂度取决于排序过程中用到的辅助存储空间。在基数排序中,需要开辟多个桶(一般为10个)以及一个数组来存储排序结果,因此空间复杂度为O(n+k),其中n为待排序数组的长度,k为桶的个数。

3、算法稳定性

 基数排序不会破坏相等元素的相对顺序,所以是稳定的。


相关文章
|
7月前
|
搜索推荐 算法 Java
sort-03-SelectSort 选择排序算法详解
这是一个关于排序算法的系列文章摘要,包括了各种排序算法的介绍和Java实现。文章列举了冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序的链接和简要说明。其中,选择排序算法被详细解释,它是通过找到未排序序列中的最小元素并将其放到正确位置来逐步构建有序序列的。Java实现中,选择排序的`doSort`方法通过两层循环实现,时间复杂度为`O(N^2)`,是稳定的排序算法。整个排序项目已开源在GitHub。
|
7月前
|
搜索推荐 算法 Java
sort-01-bubble sort 冒泡排序算法详解
这是一个关于排序算法的系列文章摘要。作者整理了10种不同的排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序和大文件外部排序。文章详细介绍了冒泡排序的工作原理、流程,并提供了代码实现,强调了在实现中考虑的改进点,如统一接口、实用性增强和日志输出。此外,还提供了一个排序接口和工具类以方便使用,并通过测试代码和日志展示了排序过程。整个系列旨在帮助读者理解和掌握排序算法。相关代码已开源在GitHub。
|
7月前
|
搜索推荐 算法 Java
sort-02-QuickSort 快速排序到底快在哪里?
这是一个关于排序算法的系列文章的摘要。文章详细介绍了各种排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及处理大文件的外部排序。特别强调了快速排序,它是1959年由Tony Hoare发明的,平均时间复杂度为O(n log n),优于其他如合并排序和堆排序。快速排序采用分而治之的策略,选取基准元素,通过分区操作将数组分成两部分,然后递归地对这两部分进行排序。文章还通过示例和Java代码解释了快速排序的步骤和实现。最后,提供了开源项目的链接,供读者进一步学习和研究。
|
7月前
|
搜索推荐 算法 Java
sort-08-counting sort 计数排序
这是一个关于排序算法的系列文章摘要。文章详细介绍了多种排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序。计数排序是一种线性时间复杂度的稳定排序算法,由 Harold H. Seward 在1954年提出。基础版计数排序通过创建一个与最大元素等长的新数组来统计每个元素出现的次数,然后填充排序结果。改良版则考虑了空间浪费问题,通过找出最小值来减少数组长度。此外,还提出了使用 TreeMap 来扩展排序算法以适应非数字元素的情况。
|
7月前
|
存储 搜索推荐 算法
sort-09-bucket sort 桶排序
这是一个关于排序算法的系列文章总结,包括冒泡、快速、选择、堆、插入、希尔、归并、计数、桶和大文件外部排序。桶排序是一种将元素分配到有限数量的桶中,然后对每个桶分别排序的算法。在元素均匀分布的情况下,桶排序的时间复杂度为线性`O(n)`。文章还提供了一个Java实现示例及复杂度分析。完整代码可在GitHub的开源项目中找到。
|
搜索推荐 JavaScript 前端开发
基数排序(Radix Sort)
基数排序(Radix Sort)是一种非比较排序算法,它根据数字的每一位(从最低位到最高位)进行排序,具体来说,它是将所有待排序的数字统一为同样的数位长度,然后从最低位开始,依次对每个数位进行排序,最后将所有数字按照数位从低到高的顺序合并成一个有序数组。
72 6
|
存储 搜索推荐 算法
计数排序(Counting Sort)详解
计数排序(Counting Sort)是一种非比较排序算法,其核心思想是通过计数每个元素的出现次数来进行排序,适用于整数或有限范围内的非负整数排序。这个算法的特点是速度快且稳定,适用于某些特定场景。在本文中,我们将深入探讨计数排序的原理、步骤以及性能分析。
269 1
计数排序(Counting Sort)详解
|
存储 算法 搜索推荐
python实现【计数排序】(Count Sort)
python实现【计数排序】(Count Sort)
python实现【计数排序】(Count Sort)
|
算法 搜索推荐
经典算法之折半插入排序(Binary Insertion Sort)
经典算法之折半插入排序(Binary Insertion Sort)
324 0
2-路插入排序(Two-Way Insertion Sort)
算法介绍 算法描述 算法分析 代码实现 参考