数据结构【堆排序】

前言

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

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;
}

结语

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

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

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

相关文章

Redis系列-4 Redis集群介绍

Redis集群 Redis提供了持久化能力&#xff0c;保证了重启不会丢失数据&#xff1b;但Redis重启至完全恢复期间&#xff0c;缓存不可用。另外&#xff0c;对于高并发场景下&#xff0c;单点Redis服务器的性能不能满足吞吐量要求&#xff0c;需要进行横向扩展。此时&#xff0c;…

Java基础_Stream流

Java基础_Stream流 Stream流的简单使用Stream流的获取Stream流的中间方法Stream流的终结方法综合练习数字过滤字符串过滤并收集自定义对象过滤并收集 来源Gitee地址 Stream流的简单使用 public class StreamDemo01 {public static void main(String[] args) {/*** 创建集合添加…

【C++ | 拷贝赋值运算符函数】一文了解C++的 拷贝赋值运算符函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-06-09 1…

API接口测试工具:jmeter的安装、汉化、Jmeter桌面快捷图标和基本使用

文章目录 测试工具&#xff1a;JmeterJmeter安装和配置Jmeter汉化设置中文语言&#xff1a;永久方式设置中文语言&#xff1a;临时方式 设置Jmeter桌面快捷图标jmeter基本用法Jmeter无法保存测试问题解决 测试工具&#xff1a;Jmeter Jmeter依赖于JDK&#xff0c;所以必须确保…

kafka集成flink api编写教程

1.引入依赖&#xff08;pox.xml&#xff09; <dependencies><dependency><groupId>org.apache.flink</groupId><artifactId>flink-java</artifactId><version>1.13.6</version></dependency><dependency><gro…

C# WPF入门学习主线篇(十六)—— Grid布局容器

C# WPF入门学习主线篇&#xff08;十六&#xff09;—— Grid布局容器 欢迎来到C# WPF入门学习系列的第十六篇。在前几篇文章中&#xff0c;我们已经探讨了 Canvas、StackPanel、WrapPanel 和 DockPanel 布局容器及其使用方法。本篇博客将介绍另一种功能强大且灵活的布局容器—…

MT76X8 RF定频使用方法

一、从下面网址下载QA软件包&#xff0c;然后在WIN系统下安装QA环境。https://download.csdn.net/download/zhouwu_linux/89408573?spm1001.2014.3001.5503 在WINDOWS 7系统下先安装WinPcap_4_1_3.exe。 二、硬件连接。 模块上电&#xff0c;PC机 的IP配置成为10.10.18.100&a…

GraphQL(6):认证与中间件

下面用简单来讲述GraphQL的认证示例 1 实现代码 在代码中添加过滤器&#xff1a; 完整代码如下&#xff1a; const express require(express); const {buildSchema} require(graphql); const grapqlHTTP require(express-graphql).graphqlHTTP; // 定义schema&#xff0c;…

Wireshark TS | 应用传输丢包问题

问题背景 仍然是来自于朋友分享的一个案例&#xff0c;实际案例不难&#xff0c;原因也就是互联网线路丢包产生的重传问题。但从一开始只看到数据包截图的判断结果&#xff0c;和最后拿到实际数据包的分析结果&#xff0c;却不是一个结论&#xff0c;方向有点跑偏&#xff0c;…

微服务第一轮

课程文档 目录 一、业务流程 1、登录 Controller中的接口&#xff1a; Service中的实现impl&#xff1a; Service中的实现impl所继承的接口IService&#xff08;各种方法&#xff09;&#xff1a; VO&#xff1a; DTO&#xff1a; 2、搜索商品 ​Controller中的接口&a…

牛客热题:最长公共子串

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;最长公共子串题目链接方法一&am…

【Redis学习笔记05】Jedis客户端(中)

Jedis客户端 1. 命令 1.1 String类型 1.1.1 常见命令 SET命令 语法&#xff1a;SET key value [EX seconds | PX milliseconds] [NX|XX] 说明&#xff1a;将string类型的value值设置到指定key中&#xff0c;如果之前该key存在&#xff0c;则会覆盖原先的值&#xff0c;原先…

Java--可变参数

1.JDK1.5开始&#xff0c;Java支持同类型的可变参数给一个方法 2.在方法声明之前&#xff0c;在指定参数类型后加一个省略号&#xff08;...&#xff09; 3.一个方法只能指定一个可变参数&#xff0c;它必须是方法的最后一个参数&#xff0c;任何普通的参数必须在它之前声明 …

Java----抽象类和接口

欢迎大家来这次博客-----抽象类和接口。 1.抽象类 1.1 抽象类概念 在Java中我们都是通过类来描述对象&#xff0c;但反过来并不是所有的类都是用来描述对象的。当一个类中没有足够的信息来描述一个具体对象&#xff0c;我们就将该类称为抽象类。 如上图中的Shape类&#xff…

支付宝订单支付和超时收单

下订单成功后&#xff0c;发送mq消息到死信队列&#xff0c;消息过期后根据死信的路由key重新路由到交换机&#xff0c;交换机根据消息的路由key把消息路由到普通队列上&#xff0c;消费者监听队列并消费。 问题&#xff0c;现在提交订单信息调用支付宝的支付页面&#xff0c;…

AI论文速读 | 2024[ICML]FlashST:简单通用的流量预测提示微调框架

题目&#xff1a; FlashST: A Simple and Universal Prompt-Tuning Framework for Traffic Prediction 作者&#xff1a;Zhonghang Li, Lianghao Xia&#xff08;夏良昊&#xff09;, Yong Xu&#xff08;徐勇&#xff09;, Chao Huang 机构&#xff1a;华南理工大学&#xf…

分享不用会员免费听歌的软件,可听付费,支持随听随下!

今天来点特别的&#xff0c;给你们带来几款全网免费听歌的神器&#xff0c;让你们的音乐之旅不再有障碍&#xff01; 现在&#xff0c;找好听的歌越来越像寻宝一样&#xff0c;动不动就得掏腰包。不过别担心&#xff0c;阿星今天就来分享几款好用的免费听歌app&#xff0c;电脑…

SQL(一)基本语法

文章目录 一、Sql 语言基本特点二、数据查询&#xff08;按执行顺序排列&#xff09;1. From & Join2. Where3. Group by4. Having5. Select6. Distinct7. Order by8. Limit/ Offset 三、功能公式1. 字符处理2. 时间处理3. 统计计算 一、Sql 语言基本特点 不区分大小写分号…

数据库(29)——子查询

概念 SQL语句中嵌套SELECT语句&#xff0c;称为嵌套查询&#xff0c;又称子查询。 SELECT * FROM t1 WHERE column1 (SELECT column1 FROM t2); 子查询外部语句可以是INSERT/UPDATE/DELETE/SELECT的任何一个。 标量子查询 子查询返回的结果是单个值&#xff08;数字&#xff…

pdf压缩文件怎么压缩最小,软件工具压缩清晰

PDF格式的文件&#xff0c;当其体积过于庞大时&#xff0c;确实在上传的过程中显得尤为不便。今天给大家分享一个压缩pdf的简单的方法&#xff0c;让大家可以轻松的压缩pdf。 浏览器打开 "轻云处理pdf官网" &#xff0c;上传pdf文件&#xff0c;文件上传完成后网站会…