基数排序详解(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、算法稳定性

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


相关文章
|
Ubuntu
Ubuntu无法找到add-apt-repository问题的解决方法
Ubuntu无法找到add-apt-repository问题的解决方法
456 1
|
关系型数据库 MySQL C语言
gcc版本过低导致charconv: No such file or directory
gcc版本过低导致charconv: No such file or directory
2033 0
|
存储 Kubernetes Docker
k8s--pod 介绍
k8s--pod 介绍
k8s--pod 介绍
|
6月前
|
人工智能 算法 计算机视觉
只需完成手画线稿,让AI算法帮你自动上色
本文介绍了如何利用图像处理技术生成手绘风格图像及自动上色的方法。内容涵盖图像灰度化、梯度调整、虚拟深度实现手绘效果,以及使用 Python 编程实现相关算法。此外,还介绍了 AI 工具 Style2Paints V4.5,其可为线稿自动上色并支持多种线稿类型,如插画和手绘铅笔稿,适用于艺术创作与图像处理领域。
|
12月前
|
人工智能 缓存 并行计算
FlashMLA:DeepSeek最新开源!MLA解码内核让NVIDIA Hopper开启性能狂暴模式,推理速度飙升至3000GB/s
FlashMLA 是 DeepSeek 开源的高效 MLA 解码内核,专为 NVIDIA Hopper 架构 GPU 优化,支持 BF16 精度和页式 KV 缓存,适用于大语言模型推理和自然语言处理任务。
463 2
|
NoSQL 程序员 Linux
轻踩一下就崩溃吗——踩内存案例分析
踩内存问题分析成本较高,尤其是低概率问题困难更大。本文详细分析并还原了两个由于动态库全局符号介入机制(it's a feature, not a bug)触发的踩内存案例。
|
数据采集
以“雪球网行情中心板块数据抓取”的爬虫案例
爬虫案例—雪球网行情中心板块数据抓取
807 1
|
安全 网络安全 Windows
Serv-U无法开启后台模式,如何处理?
Serv-U无法开启后台模式,如何处理?
409 14
|
存储 监控 数据可视化
《惊爆!SLS 底层存储之谜大揭秘,竟不是 OSS?!真相令人瞠目结舌!》
【8月更文挑战第15天】在数字化时代,高效管理日志数据至关重要。阿里云日志服务(SLS)提供强大日志管理,支持数据收集、存储、查询与分析。不同于通用对象存储服务(OSS),SLS采用专为日志优化的存储架构,确保高效写入与快速检索。用户仅需调用SLS接口即可轻松管理日志,无需关注底层细节或自行编写复杂代码。SLS通过简化流程,为企业提供专业高效的日志服务解决方案。
364 4
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。

热门文章

最新文章