暴力数据结构之二叉树(堆的相关知识)

1. 堆的基本了解

        堆(heap)是计算机科学中一种特殊的数据结构,通常被视为一个完全二叉树,并且可以用数组来存储。堆的主要应用是在一组变化频繁(增删查改的频率较高)的数据集中查找最值。堆分为大根堆和小根堆,大根堆中任意节点的值都大于其子树中节点的值,而小根堆则相反。堆的存储方式遵循层序遍历的规则,这样可以高效地利用存储空间。在数组中,根节点的下标为0,节点的左右孩子的下标可以通过特定的公式计算得出。堆的实现通常利用动态数组,这样可以快速扩展容量而不造成空间浪费。

堆的一些性质:1.堆中某个结点的值总是不大于或不小于其父结点的值;

                         2.堆总是一棵完全二叉树。

2. 堆的实现

 我们知道堆的逻辑结构是一个完全二叉树,但是其物理结构仍然是一个数组,所以实现堆创建一个数组即可。

typedef int HPDateType;

typedef struct Heap
{
	HPDateType* a;
	int size;
	int capacity;
}HP;

void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;
}
void HPDesTroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

void Swap(HPDateType* p1, HPDateType* p2)
{
	HPDateType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(HPDateType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HPPush(HP* php, HPDateType x)
{
	if (php->capacity == php->size)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * newcapacity);
		if (tmp = NULL)
		{
			perror("realloc");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}
void AdjustDown(HPDateType* 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[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

HPDateType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
2.1 堆的插入

大堆的父节点均大于子节点,小堆恰好相反,自然实现逻辑各不相同,这里主要有两个主要的思想就是"父子值交换","父子址交换"。解释就是(以小堆为例):

如果对一个已有的小堆插入新的数据(叶子),如果这个叶子与他的父节点相比更小,就与父节点交换,再与交换后节点所属的父节点对比,如果还是小于就继续交换。

 代码解释如下: 

当要插入数据时先将其放在叶子节点(数组尾部),通过AdjustUp函数可以实现向上比较的动作,当然插入前判断空间是否充足,适当扩容即可。

void AdjustUp(HPDateType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HPPush(HP* php, HPDateType x)
{
	if (php->capacity == php->size)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDateType* tmp = (HPDateType*)realloc(php->a, sizeof(HPDateType) * newcapacity);
		if (tmp = NULL)
		{
			perror("realloc");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}
2.2 堆的删除

堆的删除就是将根节点与叶子节点交换后直接删除交换后的叶子节点(即最初的跟节点数据),然后将交换后的根节点逐渐向下交换,如图所示:

 通过代码展示就是,先交换根节点与叶子结点(即数组头尾交换)然后直接删除交换后的叶子结点。创建一个AdjustDown函数,逐层下沉交换后的跟节点,保持仍然是一个堆。

void AdjustDown(HPDateType* 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[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

3.堆排序

主要思路:升序建大堆,降序建小堆

解释:以升序为例,创建一个大堆,即根节点为最大的数据,此时要排序,就直接将根节点与叶子结点交换(数组首尾交换),然后数组末尾的下标向前移动(此时数组末尾的数据不会参与后续运算),然后将交换后的跟节点使用AdjustDown函数下沉,以此类推,最后原数组就是一个升序排列。同理小堆也是如此。

为什么升序不用小堆呢,因为小堆每一次运算都要再次创建一个数组,浪费更多的内存,可以使用但是不推荐。同理这也是为什么降序使用小堆。

具体代码如下

void HeapSort(int* a, int n)
{
	// 降序,建小堆
	// 升序,建大堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

	

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

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

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

相关文章

Pathlib,一个不怕迷路的 Python 向导

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;关注我&#xff0c;收看每期的编程干货。 一个简单的库&#xff0c;也许能够开启我们的智慧之门&#xff0c; 一个普通的方法&#xff0c;也许能在危急时刻挽救我们于水深火热&#xff0c; 一个新颖的思维方式&#xff0c;也许能…

vs2019 c++里用 typeid() . name () 与 typeid() . raw_name () 测试数据类型的区别

&#xff08;1&#xff09; 都知道&#xff0c;在 vs2019 里用 typeid 打印的类型不大准&#xff0c;会主动去掉一些修饰符&#xff0c; const 和引用 修饰符会被去掉。但也可以给咱们验证学到的代码知识提供一些参考。那么今天发现其还有 raw_name 成员函数&#xff0c;这个函…

pandas 读取Excel中有行名、列名的数据中的每个元素

读取Excel中有行名、列名的数据中的每个元素,使用pandas,Excel中的内容示例如下&#xff1a; 读取代码如下&#xff1a; def read_xlsx(file ):""" Excel矩阵数据读取 """try:df pd.read_excel(file)# 使用iterrows()方法迭代行for index, ro…

gitlab webhook触发jenkins任务

配置jenkins 安装gitlab插件 配置jenkins job 选择gitlab webhook触发 在高级中生成token 代码仓设置 新增webhook 配置webhook 测试连接 缺点&#xff0c;不能带gitLab事件的参数&#xff01;&#xff01;&#xff01;

如何向全国各大新闻网站投稿?

在信息爆炸的时代,新闻媒体的投稿工作对于单位的信息宣传员来说,既是一项重要的职责,也是一项充满挑战的任务。作为一名信息宣传员,我负责着单位的对外信息宣传投稿工作,每个月都需要在各大媒体上发表文章,以展示单位的成果和风采。 然而,刚开始的投稿之路并不顺畅。我习惯性地…

聊聊数据库索引

一、索引类型介绍 索引是对数据库表中一列或多列的值进行排序的一种结构。 一个非常恰当的比喻就是书的目录页与书的正文内容之间的关系&#xff0c;为了方便查找书中的内容&#xff0c;通过对内容建立索引是对数据库表中一列或多列的值进行排序的一种结构。 索引形成目录。索…

基于Springboot的学生心理压力咨询评判(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的学生心理压力咨询评判&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

Retrying,一个神奇优雅的 Python 库

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;关注我&#xff0c;收看每期的编程干货。 一个简单的库&#xff0c;也许能够开启我们的智慧之门&#xff0c; 一个普通的方法&#xff0c;也许能在危急时刻挽救我们于水深火热&#xff0c; 一个新颖的思维方式&#xff0c;也许能…

【ARMv8/v9 系统寄存器 6 -- EL 异常等级判定寄存器 CurrentEL 使用详细将介绍】

文章目录 ARMv8/v9 EL 等级获取EL 等级获取函数实现EL 等级获取测试 ARMv8/v9 EL 等级获取 下面这个宏定义是用于ARMv8/v9架构下&#xff0c;通过汇编语言检查当前执行在哪个异常级别&#xff08;Exception Level&#xff0c;EL&#xff09;并据此跳转到不同的标签。 异常级别…

分布式系统的一致性与共识算法(三)

顺序一致性(Sequential Consistency) ZooKeeper 一种说法是ZooKeeper是最终一致性&#xff0c;因为由于多副本、以及保证大多数成功的ZAB协议&#xff0c;当一个客户端进程写入一个新值&#xff0c;另外一个客户端进程不能保证马上就能读到这个值&#xff0c;但是能保证最终能…

Ardupilot Rpanion iperf网络性能测试

Ardupilot Rpanion iperf网络性能测试 1. 源由2. 分析3. 安装4. 测试4.1 第一次测试4.1.1 iperf测试参数A4.1.1.1 测试链路14.1.1.2 测试链路24.1.1.3 测试链路3 4.1.2 iperf测试参数B - 测试链路34.1.2.1 测试数据4.1.2.2 数据简单分析4.1.2.3 数据深入分析4.1.2.4 模拟测试网…

AI作画算法详解:原理、应用与未来发展

随着人工智能技术的不断发展&#xff0c;AI作画逐渐成为了一个热门话题。AI作画&#xff0c;即利用人工智能算法生成绘画作品&#xff0c;不仅仅是技术的展示&#xff0c;更是艺术与科技结合的创新体现。本文将深入探讨AI作画的核心算法原理&#xff0c;并通过实例帮助读者更好…

三大平台直播视频下载保存方法

终于解决了视频号下载的问题&#xff0c;2024年5月15日亲测可用。 而且免费。 教程第二部分&#xff0c;有本地电脑无法下载的解决方案。 第一部分&#xff1a;使用教程&#xff08;正常&#xff09; 第1步&#xff1a;下载安装包 下载迅雷网盘搜索&#xff1a;大海福利合集…

Pygame中播放音频的方法

在Pygame中&#xff0c;通过mixer模块以及该模块中的music模块实现播放音频的功能。 1 mixer模块与music模块简介 mixer模块包含了用于导入与播放音频的类&#xff0c;而mixer模块中的music模块可以直接将音频文件导入到内存中用于播放。 2 mixer模块与music模块的使用 2.1…

Rust中使用Rocket框架返回html网页,返回一个基于 Handlebars (HBS) 模板的响应

在Rust中使用Rocket框架返回网页&#xff0c;通常涉及创建一个路由&#xff0c;该路由将返回一个HTML页面。Rocket是一个快速、易用且可扩展的Web框架&#xff0c;它允许你以一种简洁的方式定义路由和处理请求。 一、使用Rocket框架返回一个简单的HTML页面&#xff1a; 添加依…

Go微服务开源框架kratos的依赖注入关系总结

该文章为学习开源微服务框架kratos的学习笔记&#xff01;官方文档见&#xff1a;简介 | Kratos Kratos 一套轻量级 Go 微服务框架&#xff0c;包含大量微服务相关框架及工具。 一、Kratos 项目结构简介 通过 Kratos 工具生成的 Go工程化项目模板如下&#xff1a; applicati…

如何查看SNMP设备的OID

什么是OID和MIB OID OID 代表对象标识符。 OID 唯一地标识 MIB 层次结构中的托管对象。 这可以被描述为一棵树&#xff0c;其级别由不同的组织分配。MIB MIB&#xff08;管理信息基&#xff09;提供数字化OID到可读文本的映射。 使用MIB Browser扫描OID 我的设备是一台UPS SN…

C#语音播报(通过CoreAudioAPI完成对扬声器的控制)

1&#xff0c;效果&#xff1a; 作用&#xff1a; 可对当前内容&#xff08;例如此例中的重量信息&#xff09;进行语音合成播报 。可设置系统扬声器音量与状态(是否静音),同时根据扬声器状态同步更新当前控件状态与值&#xff0c;实现强制PC扬声器按照指定的音量进行播报&…

原生IP介绍

原生IP&#xff0c;顾名思义&#xff0c;即初始真实IP地址&#xff0c;是指从互联网服务提供商获得的IP地址&#xff0c;IP地址在互联网与用户之间直接建立联系&#xff0c;不需要经过代理服务器代理转发。 原生IP具备以下特点。 1.直接性 原生IP可以直接连接互联网&#xff…

Docker mysql主从同步

1. 在主节点注册一个账号&#xff0c;用于子节点访问主节点 #mysql 1主2从&#xff0c;先创建主节点 ,注意 \ 后面不要带空格 docker run --name mysql-m \ -v /usr/local/mysql/data:/var/lib/mysql \ -v /usr/local/mysql/conf:/etc/mysql/conf.d \ -v /usr/local/mysql/log:…