计数排序,基数排序,桶排序

目录

计数排序:

基数排序:

桶排序:

计数排序:

计数排序是一种非比较型整数排序算法,特别适用于一定范围内的整数排序。它的核心思想是使用一个额外的数组(称为计数数组)来计算每个值的出现次数,然后根据这些计数信息将所有的数字放到正确的位置上,从而实现排序。

大致流程就是这样的,我会用图和代码的思想给大家讲清楚的:

  1. 确定范围:首先遍历一遍待排序的数组,找出其中最大值和最小值,从而确定数字的范围。这是因为计数排序要求输入数据在一个已知的有限范围内。

  2. 初始化计数数组:创建一个长度为最大值与最小值之差加一的计数数组,并将所有元素初始化为0。计数数组的索引代表原始数组中的数值,值则表示该数值出现的次数。

  3. 计数:再次遍历原始数组,对于每个元素,将计数数组中对应索引的值加一,即统计每个元素出现的次数。

  4. 累加计数:对计数数组进行累加操作,使得计数数组中的每个元素变成小于等于其索引值的所有元素的累计总和。这样,计数数组中的每个位置就表示了在排序后数组中相应数值的起始位置。

  5. 重建数组:最后,从计数数组反向遍历原始数组,根据计数数组的信息将元素放到排序后数组的正确位置上。当把一个元素放到排序后数组时,相应地减少计数数组中该元素的计数,以保持计数的准确性。

第一步:创建一个数组,并且找到其中的最大值和最小值

第二步: 定义一个count数组(最大值 减去 最小值 加一),用来统计每个数字出现的次数

 第三步:遍历count数组,然后按照大小顺序把依次放回array数组里面去(可以理解成覆盖了原数组),最后出来的结果就可以按照大小顺序看到每一个数字出现的多少次

最后出来的结果就是:[1, 1, 2, 3, 4, 5, 5, 6, 8, 9, 9]

public static int[] countSort(int[] array){int minval = array[0];int maxval = array[0];for(int i = 0;i<array.length;i++){if(array[i] < minval){minval = array[i];}if(array[i] > maxval){maxval = array[i];}}int[] count = new int[maxval-minval+1];for(int i = 0;i<array.length;i++){int val = array[i];count[val-minval]++;}//遍历计数数组int index = 0;for(int i = 0;i<count.length;i++){while(count[i]>0){array[index] = i+minval;index++;count[i]--;}}return array;}

 

时间复杂度:

  • 计数排序的时间复杂度主要由两部分组成:统计每个元素出现次数和根据统计结果重构输出数组。对于n个元素,范围在0到k之间的整数排序,计数排序的时间复杂度为O(n+k)。其中,n是数组长度,k是数组中最大值与最小值的差加1。在最好的情况下(即k接近n或n),时间复杂度接近线性,但在k远大于n时,时间复杂度会显著增长。

空间复杂度:

  • 计数排序需要额外的空间来存储每个元素的计数,这个空间取决于待排序数组中元素的范围。具体来说,需要一个大小为k+1的计数数组,其中k是数组中的最大值与最小值之差加1。因此,空间复杂度为O(k)。如果k与n同数量级,那么空间复杂度也是线性的,即O(n)。

稳定性:

  • 计数排序是一种稳定的排序算法。因为它在统计每个元素的频率之后,按照元素原来的顺序(通过第二个循环从最小元素开始逐个累加计数数组并放回原数组)将元素放回原数组,保证了相同元素的相对位置不会改变。

基数排序:

它的核心思想是将待排序的元素根据其每一位的数值进行分配,这个过程通常从最低有效位(LSD)开始,也可以从最高有效位(MSD)开始,然后按位递进,直到最高位。基数排序利用了分配式排序的策略,也被称为“桶排序”的一种推广。


第一步:首先得有一个数组然后才能对数组进行排序,找到数组中的最大值,确定最大值的位数,比如198就是3位数

第二步:新建一个buckets数组,根据个位十位百位...来存放数据

1.按照个位数排序就是这样的,然后我们按照先进先出的原则拿出来

 

2.此时的buckets数组就是:

 

3.因为最大为2位数,所以我们还得再进行一个十位的比较

4. 按照先进先出的原则拿出来就得到最终结果

private static int getMaxDigits(int[] array){int maxnum = array[0];for(int num : array){if(num > maxnum){maxnum = num;}}return (int)Math.log10(maxnum)+1;
}public static int[] radixSort(int[] array){int maxnum = getMaxDigits(array);int radix = 10;//基数是10 因为是10进制List<Integer>[] buckets = new ArrayList[radix];for(int i = 0;i < radix;i++){buckets[i] = new ArrayList<>();}for(int digit = 1;digit <= maxnum;digit++){//记得每次要把桶清空for (List<Integer> bucket : buckets) {bucket.clear();}for (int num : array) {int index = (num / (int)Math.pow(10,digit-1))%10;buckets[index].add(num);}//从桶中收集元素到数组int index = 0;for(List<Integer> bucket : buckets){for(int num : bucket){array[index] = num;index++;}}}return array;
}

 

时间复杂度:

  • 基数排序的时间复杂度主要取决于数字的位数(d,即最大数的位数)和待排序元素的数量(n)。
  • 最好、平均和最坏情况下,基数排序的时间复杂度都是O(d*(n+k)),其中k是基数(通常是10,因为基数排序常用于十进制数)。这意味着排序的总成本是元素数量乘以位数,加上每一轮分配和收集过程中桶的管理开销。当d和k相对于n较小或固定时,基数排序非常高效。

空间复杂度:

  • 空间复杂度主要来自于存储桶(或列表)的需要。基数排序需要额外的空间来存放每个基数下的元素。最坏情况下,每个元素都会进入不同的桶中,因此空间复杂度为O(n+k)。但实际上,由于基数排序是分轮进行的,每轮只需要足够的空间来存放每个桶中的元素,理想情况下空间复杂度可以近似为O(n)。但是,考虑到实际实现中可能会为每一轮都分配桶空间,总的空间复杂度还是O(n+k)。

稳定性:

  • 基数排序是稳定的排序算法。这意味着相等的元素在排序前后相对位置保持不变。这是因为基数排序是按位进行的,每一趟排序都是独立的,并且在收集阶段按照原顺序从桶中取出元素,从而保持了稳定性。

桶排序:

  1. 初始化桶:首先确定桶的数量和每个桶覆盖的数值范围。桶的数量和分布取决于待排序数据的特性,通常需要预先知道数据的大致分布情况。

  2. 分配元素到桶:遍历待排序数组,根据元素的值将它们分配到对应的桶中。分配的依据可以是元素的大小,例如,如果数据范围是0到100,可以创建10个桶,每个桶负责10个数的范围。

  3. 桶内排序:对每个非空的桶内部进行排序。可以选择插入排序、快速排序等算法,具体选择取决于桶内元素的数量和特性。

  4. 合并桶:将所有桶中的元素按照桶的顺序(通常是桶的索引)依次取出,合并到一个数组中,这样合并后的数组就是有序的。

 

代码实现: 

public static int[] bucketSort(int[] arr){int max = Integer.MIN_VALUE;int min = Integer.MAX_VALUE;for(int num : arr){max = Math.max(max,num);min = Math.min(min,num);}//初始化桶int bucketnum = (max-min)/arr.length+1;List<Integer>[] buckets = new ArrayList[bucketnum];for (int i = 0; i < bucketnum; i++) {buckets[i] = new ArrayList<>();}//将每个元素放入桶中for (int i = 0; i < arr.length; i++) {int index = (arr[i] - min) * (bucketnum - 1) / (max - min);buckets[index].add(arr[i]);}//对桶里面的每个元素进行排序,这里可以自己实现其他排序算法for (int i = 0; i < buckets.length; i++) {Collections.sort(buckets[i]);}//重新将桶中的每个元素放回到原数组中int index = 0;for (int i = 0; i < buckets.length; i++) {for(int j = 0;j<buckets[i].size();j++){arr[index] = buckets[i].get(j);index++;}}return arr;}

时间复杂度:

  • 最好情况:如果数据均匀分布在各个桶中,且桶内排序所用的算法具有良好的时间复杂度(如插入排序在小数组上接近O(n)),桶排序的整体时间复杂度可以达到O(n + k),其中n是待排序元素的数量,k是桶的数量。
  • 平均情况:同样,如果数据分布较为均匀,桶排序的时间复杂度也是O(n + k)。
  • 最坏情况:如果所有数据都集中在少数几个桶中,特别是全部集中在同一个桶里,此时桶排序的时间复杂度退化,需要对这些桶内的元素进行排序,可能会达到O(n^2),这取决于桶内排序算法的时间复杂度。

空间复杂度:

  • 桶排序的空间复杂度主要取决于桶的数量和每个桶可能存储的元素数量。最坏情况下,如果每个元素都分配到了不同的桶中,空间复杂度为O(n)加上每个桶的额外开销。通常,空间复杂度为O(n + k),其中k是桶的数量,n是数组的长度。如果桶的数量k与n成正比或者接近n,那么空间复杂度接近O(n)。

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

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

相关文章

C语言中错误处理的基本实现

引入头文件依赖&#xff1a; 标准输入输出流&#xff1a;#include <stdio.h>获取错误信息&#xff1a;#include <string.h>&#xff0c;strerror通过这个头文件获取文件流&#xff1a;#include <stdlib.h>&#xff0c;fprintf通过这个头文件获取错误编号&…

hadoop生态圈集群搭建(持续更新240512)

Hadoop生态圈 Linux1.修改ip地址2.重启network服务3.安装插件4.关闭防火墙5.创建用户6.创建目录7.修改目录的所属主和所属组为lxy8.修改主机名:hadoop102 (注意名字后面不要加空格)9.修改hosts文件10.等插件都装完后再重启Linux11.把xshell的登录用户换成lxy &#xff08;注意&…

【TC3xx芯片】TC3xx芯片时钟监控

目录 前言 正文 1.时钟监控概念 1.1 时钟监控原理 1.2时钟监控配置寄存器

Node.js 的补充适用场景

Node.js 的适用场景相当广泛&#xff0c;以下再补充一些具体的使用场景&#xff1a; 服务器端应用开发&#xff1a; Node.js特别适合于构建高性能、高并发、低延迟的服务器端程序。它可以用来开发Web服务器、API服务器、实时通讯服务器等。Node.js的高性能和事件驱动的非阻塞I…

day09-常用API异常

1.时间日期类 1.1 Date类&#xff08;应用&#xff09; 计算机中时间原点 1970年1月1日 00:00:00 时间换算单位 1秒 1000毫秒 Date类概述 Date 代表了一个特定的时间&#xff0c;精确到毫秒 Date类构造方法 方法名说明public Date()分配一个 Date对象&#xff0c;并初始化…

【大数据】HDFS

文章目录 [toc]HDFS 1.0NameNode维护文件系统命名空间存储元数据解决NameNode单点问题 SecondaryNameNode机架感知数据完整性校验校验和数据块检测程序DataBlockScanner HDFS写流程HDFS读流程HDFS与MapReduce本地模式Block大小 HDFS 2.0NameNode HANameNode FederationHDFS Sna…

使用注解的方式进行配置RabbitMQ

引入依赖&#xff1a; <dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit-test</artifactId><scope>test</scope></dependency> 配置application.yml server:port: 8082 spring:rabbitmq…

pyqt5报错:AttributeError: ‘mywindow‘ object has no attribute ‘setCentralWidget‘

第一种解决方法是&#xff1a;AttributeError: ‘mywindow‘ object has no attribute ‘setCentralWidget‘_attributeerror: mywindow object has no attribute-CSDN博客 第二种解决方法是&#xff08;推荐&#xff09;&#xff1a; 直接把这段代码复制在 ui转 py文件的后面…

什么是JVM中的程序计数器

在计算机的体系结构中&#xff1a; 程序计数器&#xff08;Program Counter&#xff09;&#xff0c;通常缩写为 PC&#xff0c;是计算机体系结构中的一个寄存器&#xff0c;用于存储下一条指令的地址。程序计数器是控制单元的一部分&#xff0c;它的作用是确保程序能够按正确…

用 Python 和 AkShare 进行个股数据清洗:简易多功能方法

标题:用 Python 和 AkShare 进行个股数据清洗:简易多功能方法 简介: 本文介绍了如何使用 Python 和 AkShare 库对个股数据进行清洗和处理。个股数据经常需要进行清洗以用于分析、建模或可视化。我们将介绍一些简单但功能强大的方法,包括数据加载、缺失值处理、重复值检测和…

心理应用工具包 psychtoolbox 绘制小球走迷宫

psychtoolbox 是 MATLAB 中的一个工具包&#xff0c;对于科研人员设计实验范式来说是不二之选&#xff0c;因为它可以操作计算机的底层硬件&#xff0c;精度可以达到帧的级别。 文章目录 一、实验目的二、psychtoolbox 的下载安装三、Psychtoolbox 的基本使用四、完整代码 一、…

不同数据类型的内部秘密----编程内幕(2)

Q&#xff1a; char类型是如何被当成int处理的&#xff1f; A: 我们可以看看char类型变量在何时才会被当做int处理. #include <stdio.h>int main() {char ch;ch a;printf("%c\n", ch);return 0; } 汇编代码如下&#xff1a; hellomain:0x100000f60 <0&…

修改了环境变量~/.bashrc后 报错 命令 “dirname” 可在以下位置找到 * /bin/dirname * /usr/bin/dirname

问题如下&#xff1a; 修改了~/.bashrc后加入了环境变量之后报错&#xff0c;如下所示 (base) jiedell:~/桌面$ source ~/.bashrc 命令 “dirname” 可在以下位置找到 * /bin/dirname * /usr/bin/dirname 由于 /usr/bin:/bin 不在 PATH 环境变量中&#xff0c;故无法找到该…

在Linux上安装并启动Redis

目录 安装gcc环境 上传redis文件方法一&#xff1a;sftp 上传redis文件方法二&#xff1a;wget 启动redis-server ctrlc关闭redis-server 参考文章&#xff1a;Linux 安装 Redis 及踩坑 - 敲代码的阿磊 - 博客园 (cnblogs.com) 准备&#xff1a;打开VMware Workstation&am…

pair对组创建

创建方式1: pair<type,type> p(value1,value2); pair<string, int> p("Tom", 20); cout << "name:" << p.first << "age:" << p.second << endl; 创建方式2: pair<type,type> pmake_pair(v…

mysql权限分类

USAGE --无权限,只有登录数据库,只可以使用test或test_*数据库 ALL --所有权限 select/update/delete/super/slave/reload --指定的权限 with grant option --允许把自己的权限授予其它用户(此用户拥有建立账号的权限) 权限级别&#xff1a; 1、. &#xff0d;&#xff0d;全…

C语法:for循环执行顺序

今天下编写代码时遇到了如下情况&#xff1a;期望是输出 i1,j2 i1,j3 i1,j4 i2,j3 int main(void) {int i,j;for(i1;i<3;i){for(j1;j!i&&j<4;j){printf("i%d,j%d\n",i,j);}}return 0; }实际输出结果&#xff1a; i2,j1 分析上述代码&#xff1a…

商务分析方法与工具(九):Python的趣味快捷-Pandas处理公司财务数据集思路

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

LangChain:大模型框架的深度解析与应用探索

在数字化的时代浪潮中&#xff0c;人工智能技术正以前所未有的速度蓬勃发展&#xff0c;而大模型作为其中的翘楚&#xff0c;以生成式对话技术逐渐成为推动行业乃至整个社会进步的核心力量。再往近一点来说&#xff0c;在公司&#xff0c;不少产品都戴上了人工智能的帽子&#…

初识C语言——第十八天

循环while/do while while 语法结构 while(表达式) 循环语句; break:在while循环中&#xff0c;break用于永久的终止循环 continue:在while循环中&#xff0c;continue的作用是跳过本次循环continue后面的代码 直接去判断部分&#xff0c;看是否进行下一次循环。 注意事项…