数据结构【堆排序】

news2024/11/25 23:50:03

前言

在上一篇文章主要讲解了二叉树的基本概念和堆的概念以及接口的实现(点此处跳转)
我们简回顾下堆的基本概念:

1.堆分为大堆和小堆

  • 大堆:父亲结点比左右孩子都大,根结点是最大的
  • 小堆:父亲结点比左右孩子都小,根结点是最小的

2.堆是一颗完全二叉树,所以堆适合使用数组来创建
3.堆仅仅约束了父子之间的大小关系,但并没有规定左右孩子之间的大小关系

一.建堆(大堆)

这里的建堆不是说创建堆这个数据结构,而是将一个无序的数组调整成堆的顺序,建堆有两种方法;向上调整建堆与向下调整建堆。

1.向上调整建堆

我们把数组的第一个元素看成一个堆,把第二个元素想象成要插入堆的数据,然后之后向上调整;然后再进行插入这样一直下去,直到调整完数组的最后一个元素,这样一个堆就建好了。

//向上调整算法
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

//向上调整建堆
void CreateHeap(int* a, int n)
{
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
}

int main()
{
	int a[] = { 5,6,8,2,3,7,10,4,9,1 };
	printf("建堆前:");
	PrintArray(a, sizeof(a) / sizeof(int));
	CreateHeap(a, sizeof(a) / sizeof(int));
	printf("建堆后:");
	PrintArray(a, sizeof(a) / sizeof(int));
	return 0;
}

在这里插入图片描述

2.向下调整建堆

向下调整有一个限制条件,左右子树必须是堆,这也就代表了向下调整不能像向上调整一样从根结点开始调整。
既让要满足左右子树都必须是堆,那我们就从最后一个父结点开始调整,因为这样左右子树都只剩下一个结点,我们可以直接将这一个结点看成堆。
在这里插入图片描述
每调整一次,就找调整完的后一个父节点,直到调整完根结点,如图:
在这里插入图片描述

//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child+1 < n && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

//向下调整创建堆
void CreateHeap(int* a, int n)
{
	for (int i = (n-1-1)/2; i >= 0; i--)
	{
		AdjustDown(a, n,i);
	}
}
int main()
{
	int a[] = { 5,6,8,2,3,7,10,4,9,1 };
	printf("建堆前:");
	PrintArray(a, sizeof(a) / sizeof(int));
	CreateHeap(a, sizeof(a) / sizeof(int));
	printf("建堆后:");
	PrintArray(a, sizeof(a) / sizeof(int));
	return 0;
}

在这里插入图片描述

3.向上调整与向下调整的对比

向下调整建堆的时间复杂度比向上调整的时间复杂度低

假设这个堆有h层,我们向下调整的时候是从第h-1层开始调整,这时的h-1层有 2 h − 2 2^{h-2} 2h2个结点,每个结点要调整一次,越往上走调整的越少,直到第一层,也就是根结点的时候,根结点要向下调整h-1次
在这里插入图片描述

这时候调整的总步数为:
T ( n ) = 2 0 ∗ ( h − 1 ) + 2 1 ∗ ( h − 2 ) + 2 2 ∗ ( h − 3 ) + . . . . . . + 2 h − 3 ∗ 2 + 2 h − 2 ∗ 1 T(n) = 2^0*(h-1) + 2^1*(h-2) + 2^2*(h-3) + ...... +2^{h-3}*2 + 2^{h-2}*1 T(n)=20(h1)+21(h2)+22(h3)+......+2h32+2h21
我们对他进行×2操作


2 T ( n ) = 2 1 ∗ ( h − 1 ) + 2 2 ∗ ( h − 2 ) + 2 3 ∗ ( h − 3 ) + . . . . . . + 2 h − 2 ∗ 2 + 2 h − 1 ∗ 1 2T(n) = 2^1*(h-1) + 2^2*(h-2) + 2^3*(h-3) + ...... +2^{h-2}*2 + 2^{h-1}*1 2T(n)=21(h1)+22(h2)+23(h3)+......+2h22+2h11
这就形成了错位,这时我们继续错位相减


2 T ( n ) − T ( n ) = 2 1 ∗ ( h − 1 ) + 2 2 ∗ ( h − 2 ) + 2 3 ∗ ( h − 3 ) + . . . . . . + 2 h − 2 ∗ 2 + 2 h − 1 ∗ 1 − [ 2 0 ∗ ( h − 1 ) + 2 1 ∗ ( h − 2 ) + 2 2 ∗ ( h − 3 ) + . . . . . . + 2 h − 3 ∗ 2 + 2 h − 2 ∗ 1 ] 2T(n)-T(n) = 2^1*(h-1) + 2^2*(h-2) + 2^3*(h-3) + ...... +2^{h-2}*2 + 2^{h-1}*1-[2^0*(h-1) + 2^1*(h-2) + 2^2*(h-3) + ...... +2^{h-3}*2 + 2^{h-2}*1] 2T(n)T(n)=21(h1)+22(h2)+23(h3)+......+2h22+2h11[20(h1)+21(h2)+22(h3)+......+2h32+2h21]

在这里插入图片描述


T ( n ) = 2 1 + 2 2 + 2 3 + . . . . . . + 2 h − 3 + 2 h − 2 + 2 h − 1 − ( h − 1 ) T(n) = 2^1+ 2^2+2^3+......+2^{h-3}+2^{h-2}+2^{h-1}-(h-1) T(n)=21+22+23+......+2h3+2h2+2h1(h1)
T ( n ) = 1 − h + 2 1 + 2 2 + 2 3 + . . . . . . + 2 h − 3 + 2 h − 2 + 2 h − 1 T(n) =1-h+2^1+ 2^2+2^3+......+2^{h-3}+2^{h-2}+2^{h-1} T(n)=1h+21+22+23+......+2h3+2h2+2h1
T ( n ) = 2 0 + 2 1 + 2 2 + 2 3 + . . . . . . + 2 h − 3 + 2 h − 2 + 2 h − 1 − h T(n)=2^0+2^1+ 2^2+2^3+......+2^{h-3}+2^{h-2}+2^{h-1}-h T(n)=20+21+22+23+......+2h3+2h2+2h1h
T ( n ) = 2 h − 1 − h T(n)=2^h-1-h T(n)=2h1h
之前在二叉树的性质里讲过,一颗满二叉的的结点数量为: 2 h − 1 2^h-1 2h1,其高度为: h = l o g 2 ( n + 1 ) h=log_2(n+1) h=log2(n+1)
将这两个结果套到 T ( n ) T(n) T(n)
T ( n ) = n − l o g 2 ( n + 1 ) ≈ n T(n) = n-log_2(n+1) \approx n T(n)=nlog2(n+1)n(时间复杂度取影响最大的因素)

向下调整建堆,是大的项×小的项,其时间复杂度为O(n)
向上调整建堆我就不细讲了,就拿一组数据就能发现为什么向上调整会比向下调整慢了,
向上调整建堆是从一个左孩子开始调整(第二层),这时是需要向上调整一次,第三层需要向上调整两次;这样一直往下直到最后一层,这时调整次数就来到了恐怖的 2 h − 1 ∗ ( h − 1 ) 2^h-1*(h-1) 2h1(h1)次,也就是是最后一层的每个结点都要调整 h − 1 h-1 h1次(最后一层的数据就占了堆的一半)
这时总步数为:
T ( n ) = 2 1 ∗ 2 + 2 2 ∗ 2 + 2 3 ∗ 3 + . . . . . . + 2 h − 2 ∗ ( h − 2 ) + 2 h − 1 ∗ ( h − 1 ) T(n) = 2^1*2+2^2*2+2^3*3+......+2^{h-2}*(h-2)+2^{h-1}*(h-1) T(n)=212+222+233+......+2h2(h2)+2h1(h1)
这时非常恐怖的数据量
直接说结论:向上调整建堆的时间复杂度为O(N*logN)
总结:向下调整建堆的时间复杂度为O(N),向上调整建堆的时间复杂度为O(N*logN),所以为了效率,更推荐使用向下调整建堆

二.堆排序

既然堆已经建好了,那我们就开始排序把。
排序的过程:

  1. 将堆顶的数据与堆底交换,将原来堆顶的数据不看做堆
  2. 重新进行向下调整

排序每次调换的都是堆顶的数据,所以当我是大堆的时候,每次被交换的数据都是在当前堆中最大的数,小堆同理。
所以当我们要排升序的时候建大堆,要排降序的时候建小堆。

//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child+1 < n && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//向下调整创建堆
void CreateHeap(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
}

//堆排序
void HeapSort(int* a, int n)
{
	CreateHeap(a, n);
	int end = n - 1;//堆底
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		
		AdjustDown(a, end, 0);
		--end;
	}
}

在这里插入图片描述

完整代码

//向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child+1 < n && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//向下调整创建堆
void CreateHeap(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
}

//堆排序
void HeapSort(int* a, int n)
{
	CreateHeap(a, n);
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		
		AdjustDown(a, end, 0);
		--end;
	}
	
}
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

int main()
{
	int a[] = { 5,6,8,2,3,7,10,4,9,1 };
	
	printf("排序前:");
	PrintArray(a, sizeof(a) / sizeof(int));
	
	//CreateHeap(a, sizeof(a) / sizeof(int));
	HeapSort(a,sizeof(a) / sizeof(int));
	
	printf("排序后:");
	PrintArray(a, sizeof(a) / sizeof(int));
	
	return 0;
}

结语

最后感谢您能阅读完此片文章,如果有任何建议或纠正欢迎在评论区留言,也可以前往我的主页看更多好文哦(点击此处跳转到主页)。
如果您认为这篇文章对您有所收获,点一个小小的赞就是我创作的巨大动力,谢谢!!!

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

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

相关文章

关于CodeCombat(沙漠)布朗噪声的攻略

关于CodeCombat(沙漠)//布朗噪声的攻略 总的来说怎么猥琐怎么来 1.走到墙角骷髅看不到的位置&#xff0c;让宠物制造噪音&#xff0c;然后英雄走过去&#xff0c;就是这样没错&#xff08;坐标之类能明白) 最后看看运行结果吧 Rec 0002 希望天天开心

CAN协议简介

协议简介 can协议是一种用于控制网络的通信协议。它是一种基于广播的多主机总线网络协议&#xff0c;常用于工业自动化和控制领域。can协议具有高可靠性、实时性强和抗干扰能力强的特点&#xff0c;被广泛应用于汽车、机械、航空等领域。 can协议采用了先进的冲突检测和错误检测…

C++系统编程篇——linux软件包管理器yum

Linux 软件包管理器yum (1)linux系统&#xff08;centos生态&#xff09; 安装方式有三种&#xff1a;源代码安装、rpm安装、yum安装&#xff08;最简单&#xff09; ls /etc/yum.repos.d/ 查看该路径下的文件 包含了用于配置 YUM 软件包管理器的仓库配置文件。这些配置文件…

QT-轻量级的笔记软件MyNote

MyNote v2.0 一个轻量级的笔记软件&#x1f4d4; Github项目地址: https://github.com/chandlerye/MyNote/tree/main 应用简介 MyNote v2.0 是一款个人笔记管理软件&#xff0c;没有复杂的功能&#xff0c;旨在提供便捷的笔记记录、管理以及云同步功能。基于Qt 6.6.3 个人开…

ASUS华硕ROG幻14Air笔记本GA403UI(UI UV UU UJ)工厂模式原厂Windows11系统安装包,带MyASUS in WinRE重置还原

适用型号&#xff1a;GA403UI、GA403UV、GA403UU、GA403UJ 链接&#xff1a;https://pan.baidu.com/s/1tz8PZbYKakfvUoXafQPLIg?pwd1mtc 提取码&#xff1a;1mtc 华硕原装WIN11系统工厂包带有ASUS RECOVERY恢复功能、自带面部识别,声卡,显卡,网卡,蓝牙等所有驱动、出厂主题…

大模型的演进之路:从萌芽到ChatGPT的辉煌

文章目录 ChatGPT&#xff1a;大模型进化史与未来展望引言&#xff1a;大模型的黎明统计模型的奠基深度学习的破晓 GPT系列&#xff1a;预训练革命GPT的诞生&#xff1a;预训练微调的范式转换GPT-2&#xff1a;规模与能力的双重飞跃GPT-3&#xff1a;千亿美元参数的奇迹 ChatGP…

(三)React事件

1. React基础事件绑定 语法&#xff1a; on 事件名称 { 事件处理程序 }&#xff0c;整体上遵循驼峰命名法 App.js //项目根组件 //App -> index.js -> public/index.html(root)function App() {const handleClick () > {console.log(button被点击了)}return (<…

Data Mining2 复习笔记6 - Optimization Hyperparameter Tuning

6. Optimization & Hyperparameter Tuning Why Hyperparameter Tuning? Many learning algorithms for classification, regression, … Many of those have hyperparameters: k and distance function for k nearest neighbors, splitting and pruning options in decis…

【JS】立即执行函数IIFE 和闭包到底是什么关系?

历史小剧场 ”我希望认您作父亲&#xff0c;但又怕您觉得我年纪大&#xff0c;不愿意&#xff0c;索性让我的儿子给您作孙子吧&#xff01;“ ----《明朝那些事儿》 什么是立即执行函数&#xff1f; 特点&#xff1a; 声明一个匿名函数马上调用这个匿名函数销毁这个匿名函数 …

湖南(品牌控价)源点调研 手机价格管理对品牌的影响分析

前言&#xff1a;手机自发明以来&#xff0c;过去一直是国际品牌占主导地位&#xff0c;从最初的爱立信、摩托罗拉&#xff0c;到后来的诺基亚、三星&#xff0c;苹果在这个手机行业里&#xff0c;竞争激励&#xff0c;没有百年企业&#xff0c;每个品牌的盛衰都有背后的历史背…

transformer中对于QKV的个人理解

目录 1、向量点乘 2、相似度计算举例 3、QKV分析 4、整体流程 (1) 首先从词向量到Q、K、V (2) 计算Q*&#xff08;K的转置&#xff09;&#xff0c;并归一化之后进行softmax (3) 使用刚得到的权重矩阵&#xff0c;与V相乘&#xff0c;计算加权求和。 5、多头注意力 上面…

VMware Fusion 如何增加linux硬盘空间并成功挂载

文章目录 0. 前言1. 增加硬盘空间2. 硬盘分区2.1 查看硬盘2.2 分区2.3 格式化2.4 挂载 3. 参考 0. 前言 如果发现虚拟机分配的硬盘不足&#xff0c;需要增加硬盘空间。本文教给大家如何增加硬盘空间并成功挂载。 查看当前硬盘使用情况&#xff1a; df -h可以看到&#xff0c…

使用 GPT-4 创作高考作文 2024年

使用 GPT-4 创作高考作文 2024年 使用 GPT-4 创作高考作文&#xff1a;技术博客指南 &#x1f914;✨摘要引言正文内容&#xff08;详细介绍&#xff09; &#x1f4da;&#x1f4a1;什么是 GPT-4&#xff1f;高考作文题目分析 ✍️&#x1f9d0;新课标I卷 人类智慧的进步&…

二次规划问题(Quadratic Programming, QP)原理例子

二次规划(Quadratic Programming, QP) 二次规划(Quadratic Programming, QP)是优化问题中的一个重要类别,它涉及目标函数为二次函数并且线性约束条件的优化问题。二次规划在控制系统、金融优化、机器学习等领域有广泛应用。下面详细介绍二次规划问题的原理和求解过程 二…

k8s学习--kubernetes服务自动伸缩之垂直伸缩(资源伸缩)VPA详细解释与安装

文章目录 前言VPA简介简单理解详细解释VPA的优缺点优点1.自动化资源管理2.资源优化3.性能和稳定性提升5.成本节约6.集成性和灵活性 缺点1.Pod 重启影响可用性2.与 HPA 冲突3.资源监控和推荐滞后&#xff1a;4.实现复杂度&#xff1a; 核心概念Resource Requests 和 Limits自动调…

多曝光融合算法(三)cv2.createAlignMTB()多曝光图像融合的像素匹配问题

文章目录 1.cv2.createAlignMTB() 主要是计算2张图像的位移&#xff0c;假设位移移动不大2.多曝光图像的aline算法&#xff1a;median thresold bitmap原理讲解3.图像拼接算法stitch4.多曝光融合工具箱 1.cv2.createAlignMTB() 主要是计算2张图像的位移&#xff0c;假设位移移动…

开发做前端好还是后端好?

目录 一、引言 二、两者的对比分析 技能要求和专业知识&#xff1a; 职责和工作内容&#xff1a; 项目类型和应用领域&#xff1a; 就业前景和市场需求&#xff1a; 三、技能转换和跨领域工作 评估当前技能&#xff1a; 确定目标领域&#xff1a; 掌握相关框架和库&a…

端午节大家都放假了吗

端午节作为中国四大传统节日之一&#xff0c;具有深厚的文化内涵和广泛的群众基础&#xff0c;因此&#xff0c;在这个节日里发布软文&#xff0c;可以围绕其传统习俗、美食文化、家庭团聚等方面展开&#xff0c;以吸引读者的兴趣。 首先&#xff0c;可以从端午节的起源和传统习…

轴承接触角和受力分析

提示&#xff1a;轴承接触角和受力分析 文章目录 1&#xff0c;接触角2&#xff0c;轴承受力分析 1&#xff0c;接触角 所谓公称接触角就是指轴承在正常状态下&#xff0c; 滚动体和内圈及外圈沟道接触点的法线与轴心线的垂直平面之间的夹角。 按滚动轴承工作时所能承受载荷的…

倩女幽魂手游攻略:云手机自动搬砖辅助教程!

《倩女幽魂》手游自问世以来一直备受玩家喜爱&#xff0c;其精美画面和丰富的游戏内容让人沉迷其中。而如今&#xff0c;借助VMOS云手机&#xff0c;玩家可以更轻松地进行搬砖&#xff0c;提升游戏体验。 一、准备工作 下载VMOS云手机&#xff1a; 在PC端或移动端下载并安装VM…