【数据结构排序算法篇】----基数排序【实战演练】

news2024/12/25 0:43:40

在这里插入图片描述

作为一名对技术充满热情的学习者,我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代,我远非专家,而是一位不断追求进步的旅行者。通过这篇博客,我想分享我在某个领域的学习经验,与大家共同探讨、共同成长。请大家以开放的心态阅读,相信你们也会在这段知识之旅中找到启示。

文章目录

  • 前言
  • 一、什么是基数排序
  • 二、基数排序可以用于排序哪些类型的数据
  • 三、如何使用基数排序进行排序
  • 四、Java面试题
  • 五、思考
  • 总结


前言

今天我来继续聊聊数据结构排序算法----基数排序


一、什么是基数排序

基数排序是一种非比较型整数排序算法,它的工作原理是按照数字的每一位来分配和收集元素。这种排序方式通常用于排序数字(尽管它也可以用于排序其他类型的数据,比如字符串),它可以处理从小到大的各个数字位,这被称作“最低位优先”(LSD)方法,或者从大到小的各个数字位,称为“最高位优先”(MSD)方法。

基数排序的基本思想是将所有待比较的数字统一为相同的位数长度,位数较短的数字前面补零。然后,从最低位开始,依次进行一次分配和收集。对于每一个位数,排序时将数字分配至对应的桶中,并按照这些桶的顺序一次性收集起来,放回原数组,这就完成了一次排序。之后,用相同的方法对更高位进行排序。这个过程一直重复,直到最高位排序完成,整个数组就变成了有序的状态。

基数排序中使用的是一种临时的存储空间,它们是按数位上每个可能的值(例如,在十进制中就是0到9)来创建的。基数排序具有如下特点:

1. 稳定性:基数排序是一种稳定的排序算法,即具备相同值的元素,在排序后保持它们原有的相对顺序。
2. 时间复杂度:基数排序的时间复杂度是O(nk),n是排序数组的长度,k是数字的最大位数。
3. 空间复杂度:由于需要额外的空间来创建“桶”,其空间复杂度大概是O(n+k)。

尽管基数排序在理论上对于某些特定类型的数据排序时非常高效,但其性能强烈依赖于数据的分布以及基数(或位的基数)。换句话说,它适合于位数较少的整数排序,当数字范围特别广时,使用传统的比较排序可能更为高效。

二、基数排序可以用于排序哪些类型的数据

基数排序最初被设计用于整数排序,因为它们具有易于定义位的特征(例如,个位、十位、百位等)。然而,基数排序也可以扩展用于任何可以被分成较小部分的数据类型,并且这些部分可以被独立排序。以下是一些可以使用基数排序的数据类型:

1. 整数:基数排序对于非负整数尤其高效,包括小范围和大范围的值。
2. 浮点数:经过适当的转换(如IEEE标准浮点数表示),我们也可以对浮点数使用基数排序。
3. 字符串:字符串可以看作由字符组成的序列,可以对字符串集合使用基数排序,例如按字典顺序排列单词。
4. 定长字符串:如电话号码、日期等,可以通过每个字符的ASCII值进行排序。
5. 复合结构:比如说含有多个字段的数据结构,如果这些字段都可以单独排序,那么整个数据结构也可以使用基数排序进行排序。

值得注意的是,基数排序对数据的格式和划分有一定的要求。排序的数据必须能够分割成可以比较和排序的“基数字”,并且排序算法必须知道从哪一位到哪一位进行排序,以及每一位的基数(如十进制中每一位的基数是10,二进制是2等)。此外,对于那些不能明显分成独立部分或其部分大小不统一的数据来说,基数排序可能并不适宜。在处理这类数据时,可能需要其他类型的排序算法,比如比较排序或者其他非比较排序算法。

三、如何使用基数排序进行排序

当然,让我们来看一个简单的基数排序示例。

假设我们有以下数组:

[170, 45, 75, 90, 802, 24, 2, 66]

对上述数组进行基数排序的步骤如下:

  1. 分别排序每个位数
    开始时,我们先对每个数的个位数进行排序。

    原始数据: [170, 45, 75, 90, 802, 24, 2, 66]
    个位数排序: [170, 90, 802, 02, 24, 45, 75, 66]
    

    注意,170和90中的“0”布置在类似“桶”的数据结构中的同一位置,802都布置在“2”的桶中,如此类推。

  2. 对十位数排序
    接着对十位数排序。对于不足十位的数,可以认为它的十位数是0。

    个位数排序: [170, 90, 802, 02, 24, 45, 75, 66]
    十位数排序: [802, 02, 24, 45, 66, 170, 75, 90]
    
  3. 对百位数排序
    最后我们对百位数排序。不足百位的认为它的百位数是0。

    十位数排序: [802, 02, 24, 45, 66, 170, 75, 90]
    百位数排序: [002, 024, 045, 066, 075, 090, 170, 802]
    

最终的排序结果(转换回没有前导零的形式)为:

[2, 24, 45, 66, 75, 90, 170, 802]

每一步的排序都是稳定的,即元素的相对位置被保持;如果它们在输入时具有相同的键值(在这里是指数字位),这对于每一轮都是正确的。在这个例子中,我们的数组是根据每个数的个位、十位、百位等按照顺序排列的。这个过程通常使用队列(桶)来收集每一位的相同数字,并以此顺序输出到下一阶段。

记住在每个步骤中,排序只影响处理的当前位。在移动到下一位之前,我们需要完整的一轮,以确保当前位已经被完全排序。在十进制的情况下,我们可能需要十个这样的“桶”来排序每个数字。对于对二进制数排序,我们只需要两个“桶”。步骤的数量取决于正在排序的项中最大位数的个数。

四、Java面试题

面试题:

提供一种基数排序的实现,可以处理负数。展示和解释如何修改传统的基数排序算法,使它能够正确地排序包含负数的整数数组。请说明您的方法,并提供清晰、优化的代码实例。

解释:

传统的基数排序算法通常只处理非负数,因为它依赖于整数的位模式来排序而不是它们的实际值。对于包含负数的数组,我们需要稍微调整算法,来保证负数可以按照其数值大小逆序摆放在正数之前。这是因为在二进制形式中,负数表示为正数的二进制补码。如果直接对这样的二进制形式排序,将会导致对大小的判断出现逻辑错误。

为了处理负数,我们可以采用以下步骤:

  1. 分离正负数:首先将数组分离成负数和非负数两个子数组。
  2. 绝对值转化:对负数部分取绝对值。
  3. 独立排序:分别对两个子数组进行基数排序。
  4. 还原负数:对排序好的负数子数组,再次取反获取它们原来的补码形式。
  5. 合并结果:合并两个子数组,先放置转换后的负数子数组(即原始的负数),再放置非负数子数组。

代码示例:

import java.util.Arrays;

public class RadixSortWithNegatives {

    // 使用基数排序算法排序负数和非负数
    public static void radixSortWithNegatives(int[] arr) {
        if (arr.length == 0) {
            return;
        }

        // 找出最大值和最小值
        int max = arr[0], min = arr[0];
        for (int i : arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }

        // 独立排序非负数和负数
        int[] from = new int[arr.length];
        int[] to = new int[arr.length];
        System.arraycopy(arr, 0, from, 0, arr.length);

        // 计算排序的总轮次,由最大值决定
        for (int r = 1; max / r > 0; r *= 10) {
            countingSort(from, to, r);
        }

        // 如果有负数存在
        if (min < 0) {
            // 反转数组以放置负数
            reverse(to);

            // 重新计算最大值(实际上是负数部分的最小值的绝对值)
            max = -min;

            // 复制负数到新的临时数组
            System.arraycopy(to, 0, from, 0, arr.length);

            // 再次进行基数排序,只针对负数
            for (int r = 1; max / r > 0; r *= 10) {
                countingSort(from, to, r);
            }

            // 再次反转已排序的负数部分以恢复正确的顺序
            reverse(to);
        }

        // 把排序的数字复制回原数组
        System.arraycopy(to, 0, arr, 0, arr.length);
    }

    // 计数排序 - 基数排序的一个轮次
    private static void countingSort(int[] from, int[] to, int r) {
        int[] count = new int[10];
        Arrays.fill(count, 0);

        // 计算出现次数
        for (int i : from) {
            ++count[absolute(i / r) % 10];
        }

        // 调整计数数组
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 根据计数数组和位值进行排序
        for (int i = from.length - 1; i >= 0; i--) {
            to[--count[absolute(from[i] / r) % 10]] = from[i];
        }

        // 复制回原数组以进行下一个轮次
        System.arraycopy(to, 0, from, 0, from.length);
    }

    // 数字的绝对值
    private static int absolute(int i) {
        return (i < 0) ? -i : i;
    }

    // 反转数组
    private static void reverse(int[] arr) {
        for (int i = 0; i < arr.length / 2; i++) {
            int temp = arr[i];
            arr[i] = arr[arr.length - 1 - i];
            arr[arr.length - 1 - i] = temp;
        }
    }

    public static void main(String[] args) {
        int[] arr = { -5, -1, 0, 3, -8, 2, 4, -2 };
        radixSortWithNegatives(arr);
        System.out.println(Arrays.toString(arr)); // [-8, -5, -2, -1, 0, 2, 3, 4]
    }
}

面试时,重要的是能够解释代码的每个部分以及它们为什么是必要的。上面的实现考虑了处理负数的特殊情况,并且在整个排序过程中保持了稳定的排序。还要注意,上述代码是为了分别展示基数排序的负数扩展的,实际应用中可能需要进一步的优化。

五、思考

  • 在基数排序中,如何对浮点数进行适当转换以便排序?

在基数排序中,浮点数的转换通常涉及将浮点数的位模式解释为整数,以便可以使用整数排序的方法对其进行排序。这种转换必须保持浮点数的顺序关系,即在转换后的整数表示中,如果一个浮点数小于另一个,那么其对应的整数也应当小于另一个整数的表示。

下面是处理IEEE标准浮点数(例如单精度或双精度浮点数)以便使用基数排序的一种方法:

  1. 分析表示:IEEE浮点数由符号位、指数位和尾数位组成。正数和负数有不同的排序方式,而对于浮点数的排序,通常会处理其二进制表示。

  2. 处理符号位:由于浮点数可以是正数或负数,我们需要一个方法区分它们,以便保持排序的稳定性。你可以通过反转正浮点数的位模式的所有位来完成这一点,同时反转负浮点数的位模式的所有位并再反转一次符号位。这样做的结果是,所有的浮点数可以被排序为其实际大小的正确顺序。

  3. 制作整数表现形式:现在每个浮点数都有一个唯一的整数表示。这使得使用基数排序变得可能,因为你可以简单地对这些整数进行排序,如同对标准整数进行排序一样。

  4. 排序:传统的基数排序过程可以作用在转换后的整数集上。按照每一位(或多个位,取决于你排序算法的基数)依次对其排序。

  5. 还原浮点数:一旦整数排序完成,将这些整数重新转换为浮点数表示即可得到正确排序的浮点数序列。

处理浮点数的基数排序需要特别注意,尤其是与符号和指数相关的边界情况(例如,处理正负零、无穷大和NaN等特殊值)。正确地处理这些情况需要详细的IEEE浮点数标准知识和对二进制数据操作的小心处理。在实际应用中,很多排序库和函数已经包含了对浮点数排序的优化处理,因此,在需要对浮点数序列进行排序时,往往可以直接使用这些现成的工具,而不是自己从头实现基数排序算法。


总结

想象一下你在图书馆里的一大堆书籍,你需要把它们按照书籍编号进行排序。这些编号是从1到999的编号,而你的任务是将书籍排列得整整齐齐。

你可以使用基数排序的方式来完成这个任务:

  1. 第一轮分类(根据编号的个位数):你将所有书籍放到10个不同的桌子上,每个桌子对应于个位数的0到9。例如,以“5”为个位数的所有编号的书籍都会放在标记为“5”的桌子上。

  2. 收集书籍:一旦书籍按个位数分类,你将所有桌子上的书籍收集起来,保持每个桌子上的顺序。

  3. 第二轮分类(根据编号的十位数):接着,你再次将收集来的书籍分散到10个桌子上,这次根据十位数。所有十位数为“1”的书籍都会放在“1”的桌子上,以此类推。

  4. 再次收集:同样,你按顺序收集所有桌子上的书籍。

  5. 第三轮分类(根据编号的百位数):最后,你将书籍根据百位数分类到10个桌子上。

  6. 最终收集:进行最后一次收集,这时所有的书籍将按照完整的编号顺序排列好。

在整个过程中,你是按照从最小的数位(个位)到最大的数位(百位)的顺序进行排序的。“十位”和“百位”的分类只有在完成了更低位数位的分类和收集后才可能进行。基数排序正是通过这样一种分层的方式,先对数字的一部分进行排序,再逐步处理更高位的部分,最后得到完全有序的序列。

感谢大家抽出宝贵的时间来阅读博主的博客,新人博主,感谢大家关注点赞,祝大家未来的学习工作生活一帆风顺,加油!!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1452892.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《古龙风云录》中的人物性格——辰雨

《古龙风云录》是一款基于古龙武侠世界设定的现代游戏作品。这款游戏借鉴了古龙笔下的诸多人物和江湖背景进行创新演绎。 在这款游戏中,主角辰雨被塑造成一名具有冷峻寡言性格的人物,与原著中某些角色如《武林外史》里的部分人物形象有异曲同工之处。从游戏剧情来看,辰雨经历…

前端小案例——购买电影票(HTML+CSS+JS, 附源码)

一、前言 实现功能&#xff1a; 这段代码实现了一个简单的电影票选座购买的功能界面。 在页面上展示了一个电影院的座位布局&#xff0c;以及右侧显示了电影信息、选座情况、票价、总计等内容。 用户可以通过点击座位来选择购买电影票&#xff0c;每个座位的状态会在点击时改…

链表OJ题目合集第一弹:移除元素,反转链表,中间结点,倒数第k个结点,合并有序链表,回文结构,相交链表判断。(C语言版,有详细解析、图示和链接)

目录 前言 1. 移除链表元素 &#xff08;1&#xff09;题目及示例 &#xff08;2&#xff09;解析 &#xff08;3&#xff09;代码 2. 反转链表 &#xff08;1&#xff09;题目及示例 &#xff08;2&#xff09;题目解析及思路 3.链表的中间结点 &#xff08;1&#…

一起学量化之RSI指标

RSI指标 Relative Strength Index,相对强弱指数(RSI),是一个衡量资产过度买入或过度卖出状态的技术指标。 1. RSI的基本概念 当RSI超过70时,通常被认为是超买状态。当RSI低于30时,通常被认为是超卖状态。RSI超过80,被认为是严重超买状态。RSI低于20,被认为是严重超卖状…

马斯克评 OpenAI 视频模型,接地气又一针见血

马斯克评 OpenAI Sora 昨天&#xff0c;OpenAI 发布了首个视频生成模型 Sora。 一位 X&#xff08;前推特&#xff09;用户分享了 Sora 官网所展示的生成视频&#xff1a;一名女子在东京街头漫步。 该用户评论称&#xff1a;"OpenAI 今天宣布了 Sora&#xff0c;它使用混合…

STM32的三种下载方式

结果jlink&#xff0c;串口&#xff0c;stlink方式都没有问题&#xff0c;是当时缩减代码&#xff0c;看真正起作用的代码段有哪些&#xff0c;就把GPIO初始化中 /*开启GPIO外部时钟*/RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE); 把开启外部时钟的代码注释掉了。…

【plt.imshow显示图像】:从入门到精通,只需一篇文章!【Matplotlib】

【plt.imshow显示图像】&#xff1a;从入门到精通&#xff0c;只需一篇文章&#xff01;【Matplotlib】 &#x1f680; 利用Matplotlib进行数据可视化示例 &#x1f335;文章目录&#x1f335; &#x1f4d8; 1. plt.imshow入门&#xff1a;认识并安装Matplotlib库&#x1f308…

Github 2024-02-17 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-17统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目3Rust项目2Jupyter Notebook项目1PowerShell项目1JavaScript项目1 Black&#xff…

AutoKeras(Python自动化机器学习)多模态数据和多任务

要点拓扑 AutoKeras 拓扑 要点 常规机器学习&#xff1a;scikit-learn示例探索性数据分析和数据预处理&#xff0c;线性回归&#xff0c;决策树图像分类ResNet模型示例&#xff0c;合成数据集DenseNet模型示例绘图线性回归和决策树模型使用Python工具seaborn、matplotlib、pan…

PCL库学习及ROS使用

PCL库学习 c_cpp_properties.json {"configurations": [{"name": "Linux","includePath": ["${workspaceFolder}/**","/usr/include","/usr/local/include"],"defines": [],"compiler…

把Activity当做dialog对话框使用

1、引言 在安卓开发中对话框的使用是不可避免的&#xff0c;但是原生的对话框用起来总感觉差点意思&#xff0c;而且位置不好控制&#xff0c;在与界面的交互上也不够灵活&#xff0c;没有像activity那样的生命周期方法&#xff0c;以至于某些特殊的功能无法实现。此时我们就希…

深度学习主流开源框架:Caffe、TensorFlow、Pytorch、Theano、Keras、MXNet、Chainer

2.6 深度学习主流开源框架 表2.1 深度学习主流框架参数对比 框架关键词总结 框架关键词基本数据结构&#xff08;都是高维数组&#xff09;Caffe“在工业中应用较为广泛”&#xff0c;“编译安装麻烦一点”BlobTensorFlow“安装简单pip”TensorPytorch“定位&#xff1a;快…

数据库MySQL中出现乱码和表格不对齐怎么解决

MySQL中出现乱码问题及解决办法&#xff1a; 情况类似&#xff1a; 首先进入到数据库中&#xff0c;命令&#xff1a;mysql -h localhost -uroot -p或者mysql -uroot -p;进入数据库后选择一个你的数据库查看表中的中文是否乱码 以上是数据库中表格出现乱码情况&#xff0c;原…

项目架构梳理

单点登录组件 CREATE TABLE user_info (id int(11) NOT NULL AUTO_INCREMENT COMMENT 自增主键,用户id,username varchar(30) NOT NULL COMMENT 用户姓名,password varchar(60) NOT NULL COMMENT 密码,token varchar(60) DEFAULT NULL COMMENT token,token_expire datetime DEF…

1.8 NLP自然语言处理

NLP自然语言处理 更多内容&#xff0c;请关注&#xff1a; github&#xff1a;https://github.com/gotonote/Autopilot-Notes.git 一、简介 seq2seq(Sequence to Sequence)是一种输入不定长序列&#xff0c;产生不定长序列的模型&#xff0c;典型的处理任务是机器翻译&#…

BUGKU-WEB game1

题目描述 题目截图如下&#xff1a; 进入场景看看&#xff1a; 是一个盖楼的游戏&#xff01; 解题思路 先看看源码&#xff0c;好像没发现什么特别的是不是要得到一定的分数才会有对应的flag&#xff1f;查看下F12&#xff0c;请求链接发现&#xff0c;这不就提示了 相…

第13章 网络 Page738~741 13.8.3 TCP/UDP简述

libcurl是C语言写成的网络编程工具库&#xff0c;asio是C写的网络编程的基础类型库 libcurl只用于客户端&#xff0c;asio既可以写客户端&#xff0c;也可以写服务端 libcurl实现了HTTP\FTP等应用层协议&#xff0c;但asio却只实现了传输层TCP/UDP等协议。 在学习http时介绍…

九大问题困扰企业财务数字化转型,你准备好解决了吗?

随着数字化浪潮的推进&#xff0c;企业财务管理也迎来了转型的关键时刻。然而&#xff0c;多年的数字化转型经验告诉我们&#xff0c;企业在这一过程中普遍面临着许多挑战和痛点。接下来&#xff0c;我们将逐一深入剖析这些痛点&#xff0c;并探讨如何有效应对。 一、数据孤岛问…

【大厂AI课学习笔记】【2.1 人工智能项目开发规划与目标】(3)数据准备初步

今天来学习数据准备。 一个AI项目要包括构建数据集、数据清理和数据融合、数据采集、特征工程、算法改进和其他步骤。 数据采集和数据清洗&#xff0c;也就是数据准备&#xff0c;要占到人工智能项目一半以上的工作量。 训练的数据量越大&#xff0c;模型越准确。 建立数据标…

一篇文章入门postmain接口测试

一、了解接口和接口测试 1、什么是接口? 电脑&#xff1a;USB&#xff0c;投影机(数据传输) 软件&#xff1a;统称APl,application,program,interface,微信提现和充值&#xff0c;支付宝支付&#xff0c;银联支付接口。(鉴权码&#xff1a;token,key,appkey) 2、接口包括&…