数据结构——计数、桶、基数排序

目录

引言

计数排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

桶排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

基数排序

1.算法思想

2.算法步骤

3.代码实现

4.复杂度分析

排序算法的稳定性

1.稳定性的概念

2.各个排序算法的稳定性

结束语


引言

目前写的排序:

数据结构——冒泡、选择、插入和希尔排序

数据结构——快速排序

数据结构——归并排序

数据结构——堆排序

有需要的可以看看。

我们接下来学习一下计数排序、桶排序、基数排序。

计数排序

计数排序(Counting Sort)是一种非基于比较的排序算法,由Harold H. Seward在1954年提出。该算法的主要优势在于其线性时间复杂度,特别适用于待排序元素为整数且范围较小的情况。

1.算法思想

计数排序的基本思想是统计数组中每个元素的出现次数,然后利用这些信息将原始序列重新组合成有序序列。它并不直接比较元素之间的大小,而是通过计算小于等于某个值的元素个数来确定该元素在排序后数组中的位置。

2.算法步骤

1.找出待排序数组中的最大值和最小值

2.创建计数数组:计数数组的长度通常设置为待排序数组中的最大值加1(如果已知范围,则可以直接设定长度为范围大小)。计数数组的初始值全部设为0。

3.统计元素出现次数:遍历待排序数组,对于数组中的每个元素,在计数数组对应下标的位置上加1,以统计该元素的出现次数。

4.修改计数数组:对计数数组进行前缀和操作,即每个位置的值变为它本身加上前一个位置的值。这样,计数数组的每个位置就代表了小于等于该下标值的元素在排序后数组中的最后位置。

5.重建输出数组:从后向前遍历待排序数组,根据计数数组中的值确定当前元素在输出数组中的位置,并更新计数数组。具体地,对于待排序数组中的每个元素,找到计数数组中对应下标的值(即该元素在排序后数组中的位置),将元素放到输出数组的相应位置,并将计数数组中对应下标的值减1。

6.输出排序后的数组:此时,输出数组就是排序后的数组。

动图演示如下:

3.代码实现

void CountSort(int* arr, int n)
{// 初始化最小值和最大值为数组的第一个元素  int min = arr[0];int max = arr[0];// 遍历数组,找到最小值和最大值  for (int i = 1; i < n; i++){if (arr[i] < min)min = arr[i]; // 更新最小值  if (arr[i] > max)max = arr[i]; // 更新最大值  }// 计算值的范围(即最大值和最小值之间的差值加1)  int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if (count == NULL){perror("calloc fail:");return;}// 统计每个元素的出现次数  // 通过数组下标转换(arr[i] - min)将原始数组的值// 映射到计数数组的索引上  for (int i = 0; i < n; i++){count[arr[i] - min]++;}// 根据计数数组中的计数,重建排序后的数组  // 遍历计数数组,每次将对应计数值数量的元素放到arr中  int index = 0; for (int i = 0; i < range; i++){// 当count[i]大于0时,说明有i+min这个值的元素需要被放置  while (count[i]--){arr[index++] = i + min; // 放置元素,并更新index  }}free(count);count = NULL; 
}

测试一下:

4.复杂度分析

时间复杂度:由于遍历了原数组与range数组,因此时间复杂度为O(n+range)。

空间复杂度:开辟了大小为range的数组,所以空间复杂度为O(range)。

桶排序

桶排序(Bucket Sort)是一种基于分配策略的排序算法。

1.算法思想

桶排序的算法思想是将数组中的元素分配到有限数量的桶(或称为箱)里,每个桶再分别进行排序(可能使用其他排序算法或递归使用桶排序),最后合并成结果序列。

2.算法步骤

1.初始化桶:首先,需要确定桶的数量及每个桶对应的数据范围。这通常与待排序数据的范围有关,目的是使每个桶尽可能均匀地包含一部分数据。

2.分配元素:遍历待排序序列,将每个元素根据其值放入对应的桶中。这一步相当于对数据进行初步的划分和聚集。

3.桶内排序:对每个非空桶内部使用其他排序算法(如插入排序、快速排序等)进行排序,确保每个桶内的数据有序。

4.合并桶:按照桶的顺序,依次从每个桶中取出元素,合并成一个有序序列,即完成整个数据集的排序。

动图演示:

PS:这里的位数桶只是方便演示。

3.代码实现

桶内的排序我们同样可以借助插入排序来实现。

代码如下:

#define BUCKET_SIZE 10  // 定义桶的数量  
#define ARRAY_SIZE 30   // 定义数组的大小  void InsertSort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0){if (tmp < arr[end]){arr[end + 1] = arr[end];--end;}else{break;}}arr[end + 1] = tmp;}
}// 桶排序函数  
void bucketSort(int arr[], int n) 
{int bucket[BUCKET_SIZE][ARRAY_SIZE], max = arr[0], min = arr[0];int i, j, k;int b_size[BUCKET_SIZE] = { 0 }; // 每个桶的元素数量  // 找到数组中的最大值和最小值  for (i = 1; i < n; i++) {if (arr[i] > max)max = arr[i];if (arr[i] < min)min = arr[i];}// 计算桶的间隔  int range = max - min;int interval = range / BUCKET_SIZE; // 将数据分配到各个桶中  for (i = 0; i < n; i++) {int bi = (arr[i] - min) / interval; // 使用整数除法来分配桶  if (bi >= BUCKET_SIZE){bi = BUCKET_SIZE - 1;}bucket[bi][b_size[bi]++] = arr[i];}// 对每个桶进行排序  for (i = 0; i < BUCKET_SIZE; i++){InsertSort(bucket[i], b_size[i]);}// 合并桶中的数据  k = 0;for (i = 0; i < BUCKET_SIZE; i++){for (j = 0; j < b_size[i]; j++){arr[k++] = bucket[i][j];}}
}

测试一下:

4.复杂度分析

时间复杂度:

(1)如果数据均匀分布在桶中,且每个桶内能快速排序(如计数排序、插入排序等),则平均时间复杂度可以达到O(n + k),其中n是元素总数,k是桶的数量。这是因为在理想情况下,每个桶只需要处理少量元素。

(2)当输入数据已经完全有序或者极度集中在一个桶内,桶排序会退化为线性查找,此时时间复杂度为O(n^2)。

空间复杂度:由于需要额外存储每个桶以及额外的空间用于最终结果的合并,因此空间复杂度通常是O(n + k)。

基数排序

基数排序(Radix Sort)是一种非比较型整数排序算法,它通过键值的各个位的值,将要排序的元素分配到某些“桶”中,以达到排序的目的。基数排序也被称为“分配式排序”或“桶子法”(bucket sort),是桶排序的扩展。

1.算法思想

基数排序的基本原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。具体来说,它首先将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

2.算法步骤

1.计算最大数位数:首先找出待排序数组中最大数的位数。

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

3.按位排序:从最低位开始,依次进行一次排序。排序时,根据当前位的数值,将元素分配到对应的桶中。

4.合并桶中元素:将各个桶中的元素依次取出,合并成一个新的有序数组。

5.重复步骤3和4:对更高位进行排序,直到最高位排序完成。

3.代码实现

// 获取数字num在exp位上的数字
int Get_digit(int num, int exp) 
{return (num / exp) % 10;
}// 对数组arr中的数字按exp位进行计数排序
void CountSortDigit(int* arr, int n, int exp) 
{// 分配一个大小为10的数组来计数0-9每个数字出现的次数int* counter = (int*)malloc(sizeof(int) * 10);if (counter == NULL) {perror("malloc fail:");return;}// 初始化计数器数组memset(counter, 0, sizeof(int) * 10);// 遍历数组,计算每个数字在exp位上出现的次数for (int i = 0; i < n; i++) {int x = Get_digit(arr[i], exp);counter[x]++;  }// 累积计数,使counter[i]包含小于或等于i的数字的总数for (int i = 1; i < 10; i++) {counter[i] += counter[i - 1];}// 分配一个与原始数组相同大小的临时数组来存储排序后的结果int* ret = (int*)malloc(sizeof(int) * n);  if (ret == NULL) {perror("malloc fail:");return;}// 从后往前遍历原始数组,根据exp位上的数字将元素放入ret数组的适当位置for (int i = n - 1; i >= 0; i--) {int d = Get_digit(arr[i], exp);ret[counter[d] - 1] = arr[i];  counter[d]--;}// 将排序后的结果复制回原始数组for (int i = 0; i < n; i++) {arr[i] = ret[i];}free(ret);  free(counter);  
}void RadixSort(int* arr, int n) 
{// 找到数组中的最大值,以确定最大的位数int max = arr[0];for (int i = 1; i < n; i++) {  if (arr[i] > max) {max = arr[i];}}// 从个位开始,逐步向高位进行排序  // exp是当前的位数(1代表个位,10代表十位,依此类推)for (int exp = 1; max / exp > 0; exp *= 10) {// 对当前位数进行计数排序CountSortDigit(arr, n, exp);}
}

测试一下:

4.复杂度分析

时间复杂度:数据量为N、数据为D进制、最大位数为K。时间复杂度可以表示为O((N + D) * K)。但在实际应用中,由于D和K都相对较小,我们通常会将其简化为O(N * K),并进一步简化为O(N)。

空间复杂度:由于基数排序需要借助长度为N和D的统计数组,因此基数排序空间复杂度为O(N+D)。

排序算法的稳定性

1.稳定性的概念

排序算法的稳定性是指算法在排序过程中,对于具有相同关键字的元素,能否保持它们在原序列中的相对顺序不变。如果保持不变,则称该排序算法是稳定的;如果相对顺序可能发生变化,则称该排序算法是不稳定的。

2.各个排序算法的稳定性

排序算法平均时间复杂度最优时间复杂度最差时间复杂度空间复杂度稳定性
冒泡排序O(n^2)o(n)o(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
插入排序O(n^2)O(n)O(n^2)O(1)稳定
希尔排序O(n^1.3)O(n)O(n^2)O(log n)不稳定
快速排序O(n log n)O(n log n)O(n log n)O(n)不稳定
归并排序O(n log n)O(n log n)O(n log n)O(1)稳定
堆排序O(n log n)O(n log n)O(n log n)O(1)不稳定
计数排序O(n+range)O(n+range)O(n+range)O(range)稳定
桶排序O(n + k)O(n + k)O(n^2)O(n + k)稳定
基数排序O(N * K)O(N * K)O(N * K)O(N + K)稳定

结束语

排序这部分大概就学到这里。

求点赞收藏评论关注!!!

感谢各位大佬的支持!!!

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

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

相关文章

在WPF中实现多语言切换的四种方式

在WPF中有多种方式可以实现多语言&#xff0c;这里提供几种常用的方式。 一、使用XML实现多语言切换 使用XML实现多语言的思路就是使用XML作为绑定的数据源。主要用到XmlDataProvider类. 使用XmlDataProvider.Source属性指定XML文件的路径或通过XmlDataProvider.Document指定…

IDEA 系列产品 下载

准备工作 下载 下载链接&#xff1a;https://www.123865.com/ps/EF7OTd-yVHnH 仅供参考 环境 演示环境&#xff1a; 操作系统&#xff1a;windows10 产品&#xff1a;IntelliJ IDEA 版本&#xff1a;2024.1.2 注意&#xff1a;如果需要其他产品或者版本可以自行下载&#xff0…

深入理解Dubbo源码核心原理-Part3

到此开始讲解Dubbo消费端的源码 在消费一端&#xff0c;需要关注两件事情。第一&#xff0c;接口的proxy如何生成。第二&#xff0c;请求如何发送。 首先看到启动类 接下来看真正inject方法 现在需要思考&#xff0c;待注入的Bean从哪儿来&#xff0c;这个Bean必然注入的是一…

(16)MATLAB仿真Nakagami-m分布1

文章目录 前言一、Nakagami分布二、MATLAB建模代码三、仿真结果画图四、总结 前言 Nakagami衰落模型最初是由于该模型与短波电离层传播的经验结果相匹配而提出的。它还用于仿真来自多个干扰源的情况&#xff0c;因为多个独立且同分布&#xff08;i.i.d&#xff09;的瑞利分布随…

Flask-3

文章目录 ORMFlask-SQLAlchemySQLAlchemy中的session对象数据库连接设置常用的SQLAlchemy字段类型常用的SQLAlchemy列约束选项 数据库基本操作模型类定义 数据表操作创建和删除表 数据操作基本查询SQLAlchemy常用的查询过滤器SQLAlchemy常用的查询结果方法多条件查询分页器聚合…

Ajax ( 是什么、URL、axios、HTTP、快速收集表单 )Day01

AJAX 一、Ajax是什么1.1名词解释1.1.1 服务器1.1.2 同步与异步1. 同步&#xff08;Synchronous&#xff09;2. 异步&#xff08;Asynchronous&#xff09;3. 异步 vs 同步 场景4. 异步在 Web 开发中的常见应用&#xff1a; 1.2 URL 统一资源定位符1.2.1 URL - 查询参数1.2.2 ax…

JavaSE——面向对象8:Object类详解(==与equals的区别、hashCode、toString方法)

目录 一、与equals()的区别 (一)是一个比较运算符 (二)equals是Object类中的方法&#xff0c;只能判断引用类型 (三)equals方法重写练习 1.练习1 2.练习2 3.练习3 二、hashCode方法 三、toString方法 1.默认返回&#xff1a;全类名(包名类名)哈希值的十六进制 (1)不…

unreal engine5制作动作类游戏时,我们使用刀剑等武器攻击怪物或敌方单位时,发现攻击特效、伤害等没有触发

UE5系列文章目录 文章目录 UE5系列文章目录前言一、问题分析二、解决方法1. 添加项目设置碰撞检测通道2.玩家角色碰撞设置3.怪物角色碰撞预设 最终效果 前言 在使用unreal engine5制作动作类游戏时&#xff0c;我们使用刀剑等武器攻击怪物或敌方单位时&#xff0c;发现攻击特效…

基于Hive和Hadoop的用电量分析系统

本项目是一个基于大数据技术的用电量分析系统&#xff0c;旨在为用户提供全面的电力消耗信息和深入的用电量分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

Python中流行的开源OCR项目

以下是一些Python中流行的开源OCR项目&#xff1a; PaddleOCR&#xff1a;由百度开发的OCR工具库&#xff0c;支持多种语言的文字识别&#xff0c;包括中英文&#xff0c;同时支持倾斜、竖排等多种方向的文字识别。它提供了超轻量级的PP-OCRv3模型&#xff0c;适合在资源受限的…

`git restore` 和 `git checkout` 用于丢弃工作区的改动, `git switch` 和 `git checkout` 用来切换分支

git restore 和 git checkout 都可以用于丢弃工作区的改动 但它们有一些区别&#xff0c;尤其是在 Git 2.23 引入了新的命令后。 主要区别 git checkout 是一个多用途命令&#xff1a; 它用于切换分支。它还可以用于恢复工作区中特定文件的更改。由于功能过于复杂&#xff0c…

Flutter 3.24 AAPT: error: resource android:attr/lStar not found.

在Android build,gradle下面&#xff0c;添加右边红框的代码&#xff1a; subprojects {afterEvaluate { project ->if (project.plugins.hasPlugin("com.android.application") ||project.plugins.hasPlugin("com.android.library")) {project.androi…

【MySQL】SQL介绍+基础+DDL+数据备份+还原

目录 一、DDL建库建表 1. 数据库 2. 内部4特征 3. 外部4特征 4. 数据库结构 5. SQL语句分类&#xff08;重点&#xff09; 6. 注意 7. 数据库表的字段类型 8. 存储引擎 9. 数据库表的操作 二、三范式 1. 什么是范式 2. 约束作用 3. 三范式 4. 第一范式&#xff…

【Android 14源码分析】Activity启动流程-2

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

Visual Studio C# 编写加密火星坐标转换

Visual Studio C# 编写加密火星坐标转换 1、WGS84坐标转GCJ02火星坐标2、GCJ02火星坐标转WGS84坐标&#xff08;回归计算&#xff09;3、GCJ02火星坐标转BD09百度坐标4、BD09百度坐标转GCJ02火星坐标&#xff08;回归计算&#xff09;5、坐标公共转换类6、地图显示7、程序简单界…

检查jar冲突,查找存在相同class的jar

写在前面 本文看下如何查找jar冲突&#xff0c;即查找哪些jar包中存在相同的class。如果是存在相同jar的不同版本&#xff0c;基本一眼就能看出来&#xff0c;然后结合maven的依赖关系将其剔除掉即可&#xff0c;但是当你遇到了有人手动拷贝某些class到jar包中导致冲突的情况时…

【AI知识点】维度灾难(curse of dimensionality)

维度灾难&#xff08;curse of dimensionality&#xff09; 是指在处理高维数据时&#xff0c;随着维度的增加&#xff0c;数据的性质和空间结构变得越来越复杂&#xff0c;导致许多常见的算法和技术在高维空间中效率低下或效果变差的问题。 这个概念最早是由Richard Bellman在…

OpenCAEPoro安装与测试(ASC 2024)

首先简单总结一下经验&#xff1a; 在之前的测试和学习中&#xff0c;由于自己是虚拟机或者云服务器&#xff0c;有root权限&#xff0c;经常无脑增删文件&#xff08;主要是为了图省事&#xff0c;看见报错就加回来&#xff0c;多出来就删除&#xff09;&#xff0c;但是在经…

Ubuntu Server 20.04 64bit定时备份MySQL8.0.36数据库数据

一、编写sh脚本 常见备份命令介绍 我选用的是mysqldump命令&#xff0c;命令使用简介 [root]> mysqldump -helpUsage: mysqldump [OPTIONS] database_name [tables] OR mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] OR mysqldump [OPTIONS] --all…

python全栈学习记录(二十一)类的继承、派生、组合

类的继承、派生、组合 文章目录 类的继承、派生、组合一、类的继承二、派生三、组合 一、类的继承 继承是一种新建类的方式&#xff0c;新建的类称为子类&#xff0c;被继承的类称为父类。 继承的特性是&#xff1a;子类会遗传父类的属性&#xff08;继承是类与类之间的关系&a…