探索数据结构:堆,计数,桶,基数排序的分析与模拟实现

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty’s blog

1. 堆排序

1.1. 算法思想

堆排序(Heap Sort)是一种基于堆数据结构的排序算法。其核心思想是将待排序的元素构建成一个最大堆或最小堆,然后依次将堆顶元素与堆中最后一个元素交换,并重新调整堆,使得剩余元素重新满足堆的性质。重复这个过程直到所有元素都被取出,就得到了一个有序的序列。

1.2. 算法步骤

  1. 建立一个大根堆(升序)。
  2. 将堆顶元素与堆底末尾元素交换,这时待排序中最大元素成功放到正确的位置,并且将堆中待排序的元素个数size--
  3. 然后对堆顶元素进行向下调整,使剩余待排序元素重新形成一个大根堆。
  4. 重复步骤2,3直至待排序元素个数size = 1,排序完成。

img

img

为什么升序要建大堆,降序要建小堆?

因为如果升序一旦建小堆的话,每一个取堆顶的元素之后都可能会破坏原本的堆的结构,都需要重新建堆,而建堆的时间复杂度为O(N),这样N个元素的排序,时间复杂度就会劣化为O(N2 )。

1.3. 动图演示

img

1.4. 代码实现

void AdjustDown(int* arr, int n, int parent)
{int child = 2 * parent + 1;while (child < n){if (child + 1 < n && arr[child] < arr[child + 1]){child++;}if (arr[child] > arr[parent]){swap(&arr[child], &arr[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}
void HeapSort(int* arr, int n)
{//向下调整建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, n, i);}int end = n - 1;while (end > 0){swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);end--;}
}

1.5. 复杂度分析

  • 时间复杂度:向下调整建堆的时间复杂度为O(N),向下调整的时间复杂度为O(logN),一共N次。所以总时间为O(N+NlogN),复杂度为O(NlogN)。
  • 空间复杂度:没有开辟额外的空间,所以空间复杂度为O(1)。

2. 计数排序

2.1. 算法思想

**计数排序(Counting Sort)**是一种非比较性的排序算法,适用于一定范围内的整数排序。其核心思想是统计每个元素出现的次数,然后根据这个统计信息,将元素放置到正确的位置上。

2.2. 算法步骤

  1. 找出待排序数组中的最大值max和最小值min
  2. 创建一个长度为max - min + 1的计数数组count,用于存储每个元素出现的次数。
  3. 遍历待排序数组,统计每个元素出现的次数,将其存储在计数数组中相应的位置上。
  4. 根据计数数组中的统计信息,将待排序数组重新排列。
  5. 将排好序的元素从计数数组中放回待排序数组中。

img

2.3. 动图演示

img

2.4. 代码实现

void CountSort(int* arr, int n)
{//找出最大与最小元素int max = arr[0];int min = arr[0];for (int i = 0; i < n; i++){if (arr[i] > max){max = arr[i];}if (arr[i] < min){min = arr[i];}}int range = max - min + 1;int* countArray = (int*)malloc(sizeof(int) * range);if (countArray == NULL){perror("malloc fail:");return;}//初始化memset(countArray, 0, sizeof(int)*range);//统计各元素出现次数for (int i = 0; i < n; i++){countArray[arr[i] - min]++;}int j = 0;for (int i = 0; i < range; i++){while (countArray[i]--){arr[j++] = i + min;}}
}

2.5. 复杂度分析

  • 时间复杂度:遍历了原数组与range数组,所以时间复杂度为O(N+range)。
  • 空间复杂度:开辟了大小为range的数组,所以空间复杂度为O(range)。

3. 桶排序

3.1. 算法思想

桶排序(Bucket Sort) 是一种适用于一定范围内的元素排序的算法。其核心思想是将待排序的元素分配到有限数量的桶中,然后分别对每个桶中的元素进行排序,最后按照顺序将各个桶中的元素依次取出,得到有序序列。

3.2. 算法步骤

  1. 确定桶的每个桶的元素个数和桶的数量,将待排序数组中的元素分配到对应的桶中。
  • 每个桶的元素个数:bucketsize=(max-min)/n+1maxminn分别为数组最大元素,最小元素,以及元素个数。每个桶的范围就是:[min,bucketsize)[min+bucketsize,min+2*bucketsize)
  • 桶的数量:bucketnum=(max-min)/bucketsize+1bucketsize为每个桶的元素个数。
  1. 对每个桶中的元素进行排序,可以选择其他排序算法。
  2. 将各个桶中的元素按照顺序取出,组成最终的有序序列。

img

为什么bucketnumbucketsize 的计算最后要加1

  1. 首先是因为除法运算的结果是可以等于0的,而桶的数量与桶最大容纳个数是不可能为0,所以需要加1。
  2. 其次我们默认每个桶的范围是左闭右开区间,如果不加1最大的元素可能无法进入桶内。

3.3. 动图演示

img

3.4. 代码实现

void BucketSort(int* arr, int n)
{//找出最大与最小元素int max = arr[0];int min = arr[0];for (int i = 0; i < n; i++){if (arr[i] > max){max = arr[i];}if (arr[i] < min){min = arr[i];}}//每个桶的元素最大个数int bucketsize = (max - min) / n + 1;//桶的个数int bucketnum = (max - min) / bucketsize + 1;int bucket[bucketnum][bucketsize];int bucketcount[bucketnum];//每个桶当前元素个数计数器memset(bucket, 0, sizeof(bucket));memset(bucketcount, 0, sizeof(bucketcount));//将元素放入桶中for (int i = 0; i < n; i++){int index = (arr[i] - min) / bucketsize;//第几个桶bucket[index][bucketcount[index]] = arr[i];bucketcount[index]++;//第几个桶的个数++}for (int i = 0; i < bucketnum; i++){QuickSort(bucket[i], 0, bucketcount[i] - 1);}for (int i = 0; i < bucketnum; i++){int t = 0;for (int j = 0; j < bucketcount[i]; j++){arr[t++] = bucket[i][j];}}
}

3.5. 复杂度分析

  • 时间复杂度:假设有N个元素,K个桶。假设元素在各个桶内平均分布,那么每个桶内的元素数量为N/K 。假设排序单个桶使用(N/K)log(N/K)时间,则排序所有桶使用Nlog(N/K)时间。 当桶数量K比较大时,时间复杂度则趋向于O(N) 。合并结果时需要遍历所有桶和元素,时间复杂度为O(N+K)。
  • 空间复杂度:需要借助N个元素以及K个桶的辅助空间,所以空间复杂度为O(N+K)。

4. 基数排序

4.1. 算法思想

基数排序(Radix Sort)是一种非比较性的排序算法,适用于对整数或字符串等元素进行排序。其核心思想是将待排序的元素按照位数进行分组,然后依次对每个位数进行稳定的排序,最终得到有序序列。

4.2. 算法步骤

  1. 确定待排序元素的最大位数,通常通过计算最大元素的位数或者最高位数来确定。
  2. 从最低位开始,依次对元素按照当前位上的数值进行分组,并且统计每个数组出现次数记录在counter数组中。(十进制的位范围为 0~9 ,因此需要长度为 10 的统计数组)
  3. 利用前缀和counter[i] = counter[i - 1] + counter[i]求出每个对应数值的最后一个元素的下标索引。
  4. 倒序遍历,通过每个元素arr[i]的当前位上的值求出下标索引j=counter[i]-1,并将该元素存入新的数组ret[j]=arr[i]中,最后以ret数组覆盖原数组达到排序该位数的目的。
  5. 重复步骤2,3,4直至达到最大元素的位数,排序完毕。
  1. 按个位排序
    img

  2. 按十位排序
    img

为什么一定要从从最低位开始排序?

在连续的排序轮次中,后一轮排序会覆盖前一轮排序的结果。举例来说,如果第一轮排序结果a<b ,而第二轮排序结果 a>b,那么第二轮的结果将取代第一轮的结果。由于数字的高位优先级高于低位,我们应该先排序低位再排序高位。

4.3. 动图演示

img

4.4. 代码实现

//获取当前位数的值
int digit(int num, int exp) 
{return (num / exp) % 10;
}
//对当前位数进行排序
void CountSortDigit(int arr[], int n, int exp) {// 十进制的位范围为 0~9 ,因此需要长度为 10 的统计数组int* counter = (int*)malloc((sizeof(int) * 10)); if (counter == NULL){perror("malloc fail:");return;}//初始化memset(counter, 0, sizeof(int)*n);// 统计 0~9 各数字的出现次数for (int i = 0; i < n; i++) {int d = digit(arr[i], exp);counter[d]++;}// 求前缀和,将“出现个数”转换为“数组索引”for (int i = 1; i < 10; i++) {counter[i] += counter[i - 1];}// 倒序遍历,根据统计数组内统计结果,将各元素填入 retint* ret = (int)malloc(sizeof(int) * n);if (ret == NULL){perror("malloc fail:");return;}memset(ret, 0, sizeof(int) * n);for (int i = n - 1; i >= 0; i--) {int d = digit(arr[i], exp);int j = counter[d] - 1; // 获取 d 在数组中的索引 jret[j] = arr[i]; // 将当前元素填入索引 jcounter[d]--; }// 覆盖原数组for (int i = 0; i < n; i++) {arr[i] = ret[i];}
}
void RadixSort(int*arr, int n) 
{// 获取数组的最大元素,用于判断最大位数int max = arr[0];for (int  i = 0; i < n ; i++) {if (arr[i] > max) {max = arr[i];}}// 按照从低位到高位的顺序遍历for (int exp = 1; max >= exp; exp *= 10){CountSortDigit(arr, n, exp);}
}

4.5. 复杂度分析

  • 时间复杂度:设数据量为N、数据为D进制、最大位数为K ,则对某一位执行计数排序使用O(N+D) 时间,排序所有K 位使用O((N + D)K) 时间,时间复杂度为O(N*K)。通常情况下,D和K都相对较小,时间复杂度趋向O(N) 。
  • 空间复杂度:基数排序需要借助长度为N和D的统计数组,所以基数排序空间复杂度为O(N+D)。

5. 排序算法的稳定性

5.1. 稳定性的定义

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

5.2. 各种排序算法的稳定性

排序算法平均时间复杂度最好时间复杂度最坏时间复杂度空间复杂度稳定性
冒泡算法O(N2 )O(N )O(N2 )O(1 )稳定
选择算法O(N2 )O(N2 )O(N2 )O(1 )不稳定
插入排序O(N2 )O(N )O(N2 )O(1 )稳定
希尔排序O(N1.3 )O(N )O(N2 )O(1 )不稳定
快速排序O(NlogN)O(NlogN)O(N2 )O(logN )不稳定
归并排序O(NlogN)O(NlogN)O(NlogN)O(N )稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
计数排序O(N+K)O(N+K)O(N+K)O(K)稳定
桶排序O(N+K)O(N+K)O(N2 )O(N+K)稳定
N)O(N )稳定
堆排序O(NlogN)O(NlogN)O(NlogN)O(1)不稳定
计数排序O(N+K)O(N+K)O(N+K)O(K)稳定
桶排序O(N+K)O(N+K)O(N2 )O(N+K)稳定
基数排序O(N*K)O(N*K)O(N*K)O(N+K)稳定

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

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

相关文章

PPINtonus (深度学习音调分析)帕金森病早期检测系统

帕金森病&#xff08;Parkinson’s Disease&#xff0c;简称PD&#xff09;是一种主要影响运动功能的进行性神经退行性疾病。这种疾病主要是由于大脑中一个名为黑质&#xff08;substantia nigra&#xff09;的区域失去产生多巴胺的神经元而引起的。PD的主要运动症状包括震颤、…

FreeRTOS学习笔记-基于stm32(9)信号量总结(二值信号量、计数型信号量、互斥信号量、优先级翻转、优先级继承)

一、什么是信号量 信号量是一种队列&#xff0c;用于任务间同步和资源管理的机制&#xff0c;主要用来传递状态。就像是一种特殊的“旗子”或“钥匙”&#xff0c;用来在不同的任务之间进行沟通和协调&#xff0c;确保它们能够正确地配合工作&#xff0c;不会互相干扰。 二、二…

人形机器人:工业领域的得力助手

人行机器人近2年显示出强劲的增长势头&#xff0c;根据最新数据预测&#xff0c;到2026年&#xff0c;中国人形机器人产业规模将突破200亿元&#xff0c;这一数字预示着人形机器人在未来社会中将扮演更加重要的角色。 在工业领域&#xff0c;由于工业环境复杂多变&#xff0c;对…

Spring类加载机制揭秘:深度解析“卸载”阶段

1. 引言 在Spring框架中&#xff0c;类的加载和卸载是一个复杂但至关重要的过程。加载主要涉及将类的字节码加载到JVM中&#xff0c;创建对应的Class对象&#xff0c;并准备使其可用的过程。而卸载&#xff0c;则是指当一个类不再被需要时&#xff0c;将其从JVM中清除&#xf…

识别图片二维码

文档地址&#xff1a; uni-app &#xff1a; wx.previewImage(Object object) | 微信开放文档 微信小程序&#xff1a;wx.previewImage(Object object) | 微信开放文档 longpress"seeQRcode(url)”) show-menu-by-longpress"true" //识别 这个地方不触发 …

Flutter StatefulWidget 和 StatelessWidget 的区别

在 Flutter 中&#xff0c;StatefulWidget 和 StatelessWidget 是两种不同类型的 Widget&#xff0c;它们的主要区别在于它们是否能够携带和管理状态。 StatelessWidget StatelessWidget 是不可变的&#xff0c;这意味着一旦它们被创建&#xff0c;它们的属性就不能改变。它们…

网络协议二

一、套接字Socket 基于 TCP UDP 协议的 Socket 编程&#xff0c;在讲 TCP 和 UDP 协议的时候&#xff0c;我们分客户端和服务端&#xff0c;在写程序的时候&#xff0c;我们也同样这样分。 在网络层&#xff0c;Socket 函数需要指定到底是 IPv4 还是 IPv6&#xff0c;分别对应设…

基于RNN和Transformer的词级语言建模 代码分析 log_softmax

基于RNN和Transformer的词级语言建模 代码分析 log_softmax flyfish Word-level Language Modeling using RNN and Transformer word_language_model PyTorch 提供的 word_language_model 示例展示了如何使用循环神经网络RNN(GRU或LSTM)和 Transformer 模型进行词级语言建模…

三丰云免费虚拟主机及免费云服务器评测

三丰云是一家专业的云服务提供商&#xff0c;其免费虚拟主机和免费云服务器备受好评。三丰云提供稳定可靠的服务&#xff0c;完全免费的虚拟主机和云服务器让用户可以轻松搭建自己的网站或应用。自从开始使用三丰云的免费虚拟主机和免费云服务器后&#xff0c;我的网站访问速度…

thinkphp3.1中怎么使model查询以其中一个字段为key,另一个字段为值的数组?

在ThinkPHP 3.1中&#xff0c;如果你想要以一个字段作为键&#xff08;key&#xff09;&#xff0c;另一个字段作为值&#xff08;value&#xff09;来获取数组&#xff0c;可以通过查询结果集然后手动构建数组来实现。这里有一个简单的示例&#xff1a; // 假设我们有一个名为…

bash、zsh、fish三种流行Unix shell的区别

bash、zsh、fish三种流行Unix shell的区别 一、功能上的区别二、使用体验上的区别三、以下是每种 Shell 的常用命令行示例&#xff1a;BashZshFish 一、功能上的区别 bash&#xff1a;bash 是 Bourne Again SHell 的缩写&#xff0c;是 Linux 系统中默认的 Shell。bash 的特点是…

SQL性能优化 ——OceanBase SQL 性能调优实践分享(3)

相比较之前的两篇《连接调优》和《索引调优》&#xff0c;本篇文章主要是对先前两篇内容的整理与应用&#xff0c;这里不仅归纳了性能优化的策略&#xff0c;也通过具体的案例&#xff0c;详细展示了如何分析并定位性能瓶颈的步骤。 SQL 调优 先给出性能优化方法和分析性能瓶…

为什么基于 Django 和 Scrapy 的项目需要 @sync_to_async 装饰器

在现代 web 开发中&#xff0c;异步编程正变得越来越重要&#xff0c;特别是对于需要处理大量 I/O 操作的应用程序。Scrapy 是一个用于 web 抓取的异步框架&#xff0c;而 Django 是一个流行的 web 框架&#xff0c;主要采用同步编程模型。将这两个框架结合在一个项目中时&…

YT-DLP 超好用的开源视频下载工具

YT-DLP 是一个功能丰富的命令行音频/视频下载器&#xff0c;是 youtube-dl 的一个分支。由于 youtube-dl 已经停止更新&#xff0c;YT-DLP 不仅继承了其功能&#xff0c;还进行了多项改进和扩展。YT-DLP 不仅可以下载 YouTube 视频&#xff0c;还支持众多站点&#xff0c;包括国…

# RocketMQ 实战:模拟电商网站场景综合案例(二)

RocketMQ 实战&#xff1a;模拟电商网站场景综合案例&#xff08;二&#xff09; 一、SpringBoot 整合 Dubbo &#xff1a;dubbo 概述 1、dubbo 概述 Dubbo &#xff1a;是阿里巴巴公司开源的一款高性能、轻量级的 Java RPC 框架&#xff0c;它提供了三大核心能力&#xff1a…

Ubuntu系统本地搭建WordPress网站并发布公网实现远程访问

文章目录 前言1. 搭建网站&#xff1a;安装WordPress2. 搭建网站&#xff1a;创建WordPress数据库3. 搭建网站&#xff1a;安装相对URL插件4. 搭建网站&#xff1a;内网穿透发布网站4.1 命令行方式&#xff1a;4.2. 配置wordpress公网地址 5. 固定WordPress公网地址5.1. 固定地…

阿里云安装python依赖报错 Requirements should be satisfied by a PEP 517 installer.

Collecting basicsr1.4.2 (from -r requirements.txt (line 16))Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/86/41/00a6b000f222f0fa4c6d9e1d6dcc9811a374cabb8abb9d408b77de39648c/basicsr-1.4.2.tar.gz (172 kB)━━━━━━━━━━━━━━━━━━━━…

功能安全TSC

TSC 与 FSR 的基本概念 一、引言 在功能安全领域中,TSC(Technical Safety Concept,技术安全概念)和 FSR(Functional Safety Requirements,功能安全要求)是两个至关重要的概念。它们对于确保系统的安全性和可靠性起着关键作用。本文将详细阐述 TSC 和 FSR 的定义、内涵,…

QQ号码采集器

寅甲QQ号码采集软件, 一款采集QQ号、QQ邮件地址&#xff0c;采集QQ群成员、QQ好友的软件。可以按关键词采集&#xff0c;如可以按地区、年龄、血型、生日、职业等采集。采集速度非常快且操作很简单。

电能质量在线监测装置

安科瑞电气股份有限公司 祁洁 15000363176 一、装置概述 APView500电能质量在线监测装置采用了高性能多核平台和嵌入式操作系统&#xff0c;遵照IEC61000-4-30《测试和测量技术-电能质量测量方法》中规定的各电能质量指标的测量方法进行测量&#xff0c;集谐波分析、波形采…