数据结构——堆排序

什么是堆排序

堆排序就是利用堆(假设利用大堆)进行排序的算法。他的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将他移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,便能得到一个有序序列了。

建堆 

首先我们需要对数组中元素int a[] = { 6,1,2,7,9,3,4,5,10,8 }建大堆。下面我们开始建堆。

向下调整 

 我们知道完全二叉树的孩子结点child=parent*2+1;我们建大堆就需要找大的结点的值。

我们假设左结点的值大,如果右节点a[child+1]>左节点a[child],那child=child+1;即

	while ()
	{
		if (a[child + 1] > a[child])
		{
			child = child + 1;
		}
    }

接下来我们用child的值和父亲节点parent的值比较,如果,a[child]>a[parent],则交换这两个值,然后新的parent结点为child,新的child=新的parent*2+1,如果不满足这个条件的话,就跳出break。

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	
	while ()
	{
		if (a[child + 1] > a[child])
		{
			child = child + 1;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}

这个时候我们要判断这个循环什么时候停止,child每次和parent交换完都会child=parent*2+1;当child>n结点个数的时候就不需要继续再往下找大了,即循环停止。

while (child<n)

这个时候我们还需要考虑的是我们在找左节点和右节点中较大的值的时候,如果此时child==n的话,那child+1就会越界了。所以我们要加上限定条件child+1<n,即完整代码

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	
	while (child<n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child = child + 1;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}

建大堆

这个时候我们已经完成了向下调整。此时我们只需要把数组中数据建大堆即可。

首先我们要先用for循环从最后一个结点开始找他的父节点和左右节点中大的与父节点比较,所以找的第一个父节点为(n-1)-1/2;i--,直到i>=0时一直向下调整建堆。

void HeapSort(int*a,int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)//建堆
	{
		AdjustDown(a, n, i);
	}
	
}

这个时候大堆就已经建好了,此时堆顶元素是这个堆的最大值,我们用堆尾元素(end=n-1)与堆顶元素,此时最大值就来到了堆尾,然后继续向下调整保证前n-1个数还能保持大堆,再把end--,把最大的固定其位置,直到n=0时停止

int end = n - 1;
while (end > 0)
{
	Swap(&a[0], &a[end]);
	AdjustDown(a, end, 0);
	end--;
}

测试

这个时候堆排序就完整的写完啦。我们测试运行。

int main()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	HeapSort(a, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

复杂度

堆排序的效率到底有多高呢?我们来分析一下。它的运行时间主要消耗在初始构建堆和重建堆的反复筛选上。在构建堆的过程中,因为我们是完全二叉树从最下层最右边的非终端节点开始构建,将他与其孩子进行比较,若有必要进行交换,对于每个非终端结点来说,其实最多进行两次比较和呼唤操作,因次整个构建堆的时间复杂度为O(n)。

在正式排序时,第 i 次取堆顶记录重建堆需要用 O ( logi )的时间(完全二叉树的某个结点到根结点的距离为 (log i+1),并且需要取 n -1次堆顶记录,因此,重建堆的时间复杂度为 O ( nlogn )。
所以总体来说,堆排序的时间复杂度为 O ( nlogn )。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为 O ( nlogn )。这在性能上显然要远远好过于冒泡、简单选择、直接插入的 O (n2)的时间复杂度了。
空间复杂度上,它只有一个用来交换的暂存单元,也非常的不错。不过由于记录的比较与交换是跳跃式进行的,因此堆排序也是一种不稳定的排序方法。
另外,由于初始构建堆所需的比较次数较多,因此,它并不适合待排序序列个数较少的情况。

源码

#include<stdio.h>

void Swap(int* p1, int* p2)//交换
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(int* a, int n, int parent)//向下调整
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])//判断左右孩子较大的
		{
			child = child + 1;
		}
		if (a[child] > a[parent])//判断孩子结点与父节点大小
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}
void HeapSort(int*a,int n)
{
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)//建堆
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
int main()
{
	int a[] = { 6,1,2,7,9,3,4,5,10,8 };
	int n = sizeof(a) / sizeof(a[0]);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	HeapSort(a, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

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

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

相关文章

简单 Web Server 程序的设计与实现 (2024)

1.题目描述 Web 服务是 Internet 最方便与受用户欢迎的服务类型&#xff0c;它的影响力也远远超出了专业技术范畴&#xff0c; 已广泛应用于电子商务、远程教育、远程医疗与信息服务等领域&#xff0c;并且有继续扩大的趋势。目前很多 的 Internet 应用都是基于 Web 技术的&…

Java快速排序希尔排序归并排序

快速排序算法 快速排序的原理&#xff1a;选择一个关键值作为基准值。比基准值小的都在左边序列&#xff08;一般是无序的&#xff09;&#xff0c;比基准值大的都在右边&#xff08;一般是无序的&#xff09;。一般选择序列的第一个元素。 一次循环&#xff1a;从后往前比较&…

VMware中删除虚拟机

虚拟机使用完成后&#xff0c;需要删除虚拟机如何操作呢&#xff1f; 1.首先进入VMware 2.选择需要删除的虚拟机&#xff0c;点击右键 3.直接选择“移除”&#xff1f; 当然不是&#xff0c;这只是从这么目录显示中去掉了&#xff0c;并非 “真正” 删除该虚拟机 注意&#x…

使用sentinel作为熔断器

什么是sentinel Sentinel&#xff0c;中文翻译为哨兵&#xff0c;是为微服务提供流量控制、熔断降级的功能&#xff0c;它和Hystrix提供的功能一样&#xff0c;可以有效的解决微服务调用产生的“雪崩”效应&#xff0c;为微服务系统提供了稳定性的解决方案。随着Hytrxi进入了维…

labelme的json转mask,实测有效

1、创建一个conda的虚拟环境 conda creat -n labelme python3.82、转到你的标注文件夹&#xff08;包括json和图片&#xff09; cd C:/Users/Administrator/Desktop/json3、你需要在标注文件夹下用txt写下以下代码&#xff0c;并保存bat文件。 放在最后一个就可以了 echo of…

Python的核心知识点整理大全66(已完结撒花)

目录 D.3 忽略文件 .gitignore 注意 D.4 初始化仓库 D.5 检查状态 D.6 将文件加入到仓库中 D.7 执行提交 D.8 查看提交历史 D.9 第二次提交 hello_world.py D.10 撤销修改 hello_world.py 注意 D.11 检出以前的提交 往期快速传送门&#x1f446;&#xff08;在文…

微服务实战系列之Filter

前言 Filter&#xff0c;又名过滤器&#xff0c;当然不是我们日常中见到的&#xff0c;诸如此类构件&#xff1a; 而应该是微服务中常使用的&#xff0c;诸如此类&#xff08;图片来自官网&#xff0c;点击可查看原图&#xff09;&#xff1a; 一般用于字符编码转换&#xf…

MySQL--基础篇

这里写目录标题 总览MySQl各个阶段基础篇总览 MySQL概述数据库相关概念查看本机MySQL版本号启停mysql打开windows服务管理windows命令行启停 连接mysql客户端mysql运行逻辑数据模型关系型数据库 总结 SQL总览SQL通用语法SQL语句分类DDL数据库操作表操作查询表创建表结构数据类型…

【Web开发】会话管理与无 Cookie 环境下的实现策略

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; Web开发 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 问题&#xff1a; 思路&#xff1a; 方法&#xff1a; 结语 我的其他博客 前言 在当今Web应用程序中&#xff0c;会话…

C语言-第十八周做题总结-数组3

id:454 A.字符串逆序 题目描述 输入一个字符串&#xff0c;对该字符串进行逆序&#xff0c;输出逆序后的字符串。 输入 输入在一行中给出一个不超过80个字符长度的、以回车结束的非空字符串。 输出 在一行中输出逆序后的字符串。 输入样例 输出样例 题解 先用一个while…

gRCP - 面向未来的第二代 RPC 技术,解析 HTTP2.0 和 Protobuf

目录 一、gRCP - 面向未来的第二代 RPC 技术 1.1、gRPC 简介 1.1.1、gRPC 是个啥&#xff1f; 1.1.2、gRPC 核心设计思路 1.1.3、gRPC 和 ThriftRPC 区别 1.1.4、为什么使用 gRPC&#xff1f;&#xff08;好处&#xff09; 1.2、HTTP2.0 协议 1.2.1、回顾 HTTP1.0 和 H…

C# Entity Framework 中不同的数据的加载方式

延迟加载 延迟加载是指在访问导航属性时&#xff0c;Entity Framework 会自动查询数据库并加载相关数据。这种方式在我们需要访问导航属性时比较方便&#xff0c;因为我们无需手动加载相关数据&#xff0c;而且只会在需要时才会进行查询&#xff0c;从而减少了不必要的开销。但…

基于商品列表的拖拽排序后端实现

目录 一&#xff1a;实现思路 二&#xff1a;实现步骤 二&#xff1a;实现代码 三&#xff1a;注意点 一&#xff1a;实现思路 后台实现拖拽排序通常需要与前端进行配合&#xff0c;对商品的列表拖拽排序&#xff0c;前端需要告诉后端拖拽的元素和拖动的位置。 这里我们假…

【远程计算机,这可能是由于 Credssp 加客数据库修正】解决方案

1、winR打开运行窗口 输入gpedit.msc命令&#xff0c;若找不到&#xff0c;可以进行如下文件编辑格式为cmd echo offpushd "%~dp0"dir /b C:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >List.txtdir /b C:\Win…

Linux stm32串口下载程序

一、工具 使用stm32flash进行串口下载 二、stm32flash安装 sudo apt-get install stm32flash 三、查看串口设备名称 先拔掉串口运行下面指令&#xff0c;获得所有设备名称,插上串口再运行一次&#xff0c;新增的就是串口设备名称&#xff0c;记住串口设备名称&#xff0c;以…

Linux目录结构及路径描述方式

1.Linux目录结构 Linux与Windows不同&#xff0c;Linux没有盘符这个概念, 只有一个根目录 /, 所有文件都在它下面 2.Linux路径的描述方式 在Linux系统中&#xff0c;路径之间的层级关系&#xff0c;使用&#xff1a;/ 来表示 在Windows系统中&#xff0c;路径之间的层级关系…

echarts图表会残留上一条数据的折线 setOption参数的第二个坑

记一下小坑 因为我的echarts图表的 series 是循环渲染上去的 所以他可能会有一条 或多条 我展示完多条的图表后 关闭 打开单条数据的图表 发现 他会残留上一个图表的数据 显示多条 之前我还以为是后端返回错了 但是log打印和查看请求数据 确实发现是我这边的问题 原因&#…

第二百四十三回 再分享一个Json工具

文章目录 1. 概念介绍2. 分析与比较2.1 分析问题2.2 比较差异 3. 使用方法4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"相关的内容&#xff0c;本章回中将再 分享一个Json插件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我…

系列二、GitHub中的Alpha、Beta、RC、GA、Release等各个版本

一、GitHub中的Alpha、Beta、RC、GA 1.1、概述 1.2、参考 https://www.cnblogs.com/huzhengyu/p/13905129.html

软件测试/测试开发丨Pytest结合数据驱动

安装yaml pip install pyyaml pytest结合数据驱动yaml 工程目录结构 数据准备 读取excel文件 openpyxl库的安装 openpyxl库的操作 pytest结合csv实现数据驱动 csv文件介绍 pytest结合json实现数据驱动 最后感谢每一个认真阅读我文章的人&#xff0c;礼尚往来总是要有的&…