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

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

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

所属专栏:数据结构与算法
贝蒂的主页: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];
	}
	// 倒序遍历,根据统计数组内统计结果,将各元素填入 ret
	int* 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 在数组中的索引 j
		ret[j] = arr[i]; // 将当前元素填入索引 j
		counter[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)稳定

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

R语言绘图 | 双Y轴截断图

教程原文&#xff1a;双Y轴截断图绘制教程 本期教程 本期教程&#xff0c;我们提供的原文的译文&#xff0c;若有需求请回复关键词&#xff1a;20240529 小杜的生信笔记&#xff0c;自2021年11月开始做的知识分享&#xff0c;主要内容是R语言绘图教程、转录组上游分析、转录组…

【kubernetes】探索k8s集群安全机制

目录 一、认证&#xff08;Authentication&#xff09; 1.1三种认证方式 1.2需要被认证的访问类型&#xff1a; 1.3安全性说明&#xff1a; 1.4证书颁发&#xff1a; 1.5kubeconfig 1.6Service Account 1.7Secret 与 SA 的关系 1.7.1Kubernetes 设计了一种资源对象叫做…

CSS 前端面试题学习笔记2

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“前端CSS面试题-2”。 一、主要题目 1.画一条0.5px的直线 注意&#xff1a;浏览器默认最小像素单位为1px &#xff0c;小于1px的自动默认为1px。如果给0.5px,那么浏览器会直接显示为1px&#xff0c;只有通过sca…

ARM功耗管理架构演进及变迁

安全之安全(security)博客目录导读 目录 一、功耗管理架构演进及变迁概述 二、多核 三、big.LITTLE 四、DynamIQ 五、ARM-V9 DynamIQ 思考:从单核->多核->big.LITTLE->DynamIQ,功耗管理架构演进? 一、功耗管理架构演进及变迁概述 二、多核

基于jeecgboot-vue3的Flowable流程-待办任务(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、ToDo.data.ts的数据信息如下 import {BasicColumn} from //components/Table; import {FormSchema} from //components/Table; import { rules} from //utils/helper/validator; impor…

vscode ctrl+鼠标左键无法跳转

打开设置&#xff0c;搜索intel…… 将这个智能感知改成default就可以了&#xff0c;我之前是在disable处。 分析了一下&#xff0c;其实跳转功能主要是根据上下文语法分析来实现的&#xff0c;并不是简单得全文匹配&#xff0c;因此需要相关得语法分析工具。 那么为什么默认式…

源码文章上传无忧,论坛小程序支持

前言 在数字化时代&#xff0c;知识的分享与传播显得愈发重要。为了满足广大创作者和求知者的需求&#xff0c;我们推出了全新的论坛小程序&#xff0c;不仅支持文章、源码、链接等多样化内容的上传&#xff0c;还实现了付费观看功能&#xff0c;为创作者们提供了一个展示才华…

跟TED演讲学英文:Your right to repair AI systems by Rumman Chowdhury

Your right to repair AI systems Link: https://www.ted.com/talks/rumman_chowdhury_your_right_to_repair_ai_systems Speaker: Rumman Chowdhury Date: April 2024 文章目录 Your right to repair AI systemsIntroductionVocabularySummaryTranscriptAfterword Introduct…

PbootCms微信小程序官网模版/企业官网/社交电商官网/网络工作室/软件公司官网

在数字化时代&#xff0c;企业网站已成为吸引潜在客户、提升企业形象、和扩大品牌影响力的必备工具。因此&#xff0c;一个优秀的企业网站模板显得尤为重要。 企业官网的内容框架通常都包含企业形象、产品或服务类型、信息展示等部分&#xff0c;设计师需要借助和企业形象契合…

大模型的竞争格局与产品经理的未来机遇

前 言 作为产品经理&#xff0c;很重要的一点是要紧跟技术发展的潮流。大型语言模型&#xff08;LLM&#xff09;的竞争格局日新月异&#xff0c;谁会成为最终的赢家尚未可知。在这篇博文中&#xff0c;我们将介绍我们的一些重要观察发现&#xff0c;主要涉及直接面向消费者的…

2024年华为OD机试真题-多段线数据压缩-C++-OD统一考试(C卷D卷)

2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集)​ 题目描述: 下图中,每个方块代表一个像素,每个像素用其行号和列号表示。 为简化处理,多段线的走向只能是水平、竖直、斜向45度。 上图中的多段线可以用下面的坐标串表示:(2, 8), (3…

T-Pot多功能蜜罐实践@debian12@FreeBSD

T-Pot介绍 T-Pot是一个集所有功能于一身的、可选择分布式的多构架&#xff08;amd64&#xff0c;arm64&#xff09;蜜罐平台&#xff0c;支持20多个蜜罐和很多可视化选项&#xff0c;使用弹性堆栈、动画实时攻击地图和许多安全工具来进一步改善欺骗体验。GitHub - telekom-sec…

Linux Kernel nf_tables 本地权限提升漏洞(CVE-2024-1086)

文章目录 前言声明一、netfilter介绍二、漏洞成因三、漏洞危害四、影响范围五、漏洞复现六、修复方案临时解决方案升级修复方案 前言 2024年1月&#xff0c;各Linux发行版官方发布漏洞公告&#xff0c;修复了一个 netfilter:nf_tables 模块中的释放后重用漏洞&#xff08;CVE-…

若尔盖草原亲子研学营 | 八月份开启

若尔盖草原亲子研学营&#xff0c;追寻父辈记忆&#xff0c;探索绿色圣境 Following the Footsteps of our Ancestors, Exploring the Maganificent Grassland . Parent -Child Summer Camp 身处繁忙的城市生活中 您是否曾在梦中追寻父亲的足迹 渴望重温他在草原上自由驰骋的…

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

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

可视化数据科学平台在信贷领域应用系列三:特征组合

现代各企业都提倡“降本增效”&#xff0c;所以越来越多优秀的工具诞生了。若想在特征加工这块工作上提升效率&#xff0c;建模人员也能有更多时间“偷懒”&#xff0c;都 “Sora”时代了&#xff0c;为啥不巧用工具呢&#xff1f;RapidMiner在信贷风控特征加工组合中是一把利器…

【Vue】普通组件的注册使用-全局注册

文章目录 一、使用步骤二、练习 一、使用步骤 步骤 创建.vue组件&#xff08;三个组成部分&#xff09;main.js中进行全局注册 使用方式 当成HTML标签直接使用 <组件名></组件名> 注意 组件名规范 —> 大驼峰命名法&#xff0c; 如 HmHeader 技巧&#xf…

无人机推流/RTMP视频推拉流EasyDSS无法卸载软件是什么原因?

视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外&#xff0c;平台还支持用户自行上传视频文件&#xff0c;也可将上传的点播…

【文件导出3】导出xml格式文件数据

导出xml格式数据 文章目录 导出xml格式数据前言一、实现代码1.controller层2.接口层3.接口实现类4.XmlUtil 工具类 二、文件导出效果总结 前言 springBoot项目实现在线导出xml格式文件数据的功能。 一、实现代码 1.controller层 GetMapping("/record/_export") Ap…

性能工具之 JMeter 常用组件介绍(三)

文章目录 一、常用组件介绍二、Sampler&#xff1a;取样器三、Controller:控制器&#xff08;逻辑控制器&#xff09;四、Pre Processor:预处理五、Post Processor:请求之后的处理六、Assertions:断言七、Timer:定时器八、Test Fragment&#xff1a;片段九、Config Element:配置…