【数据结构】--- 堆

news2025/4/7 1:42:42

 个人主页:星纭-CSDN博客

系列文章专栏 :数据结构

踏上取经路,比抵达灵山更重要!一起努力一起进步!

目录

一.堆的介绍

二.堆的实现 

1.向下调整算法 

2.堆的创建 

 3.堆的实现

4.堆的初始化和销毁

5.堆的插入 

5.1扩容

5.2向上调整与向下调整

5.2.1向上调整

6.堆的删除 

5.2.2 向下调整

 7.取堆顶数据

8.堆的数据个数

9.判读堆是否为空 


一.堆的介绍

1.1堆的概念以及结构

简单来说,堆总是一个完全二叉树,如果每一个节点都比其子节点的值大就叫大堆,反之就叫做小堆。

二.堆的实现 

1.向下调整算法 

现在我们给出一个数组,逻辑上看做一个完全二叉树。我们通过从根节点开始的向下调整算法可以把他调整成为一个小堆。前提是:左右子树必须是一个堆,这样才能调整。

首先是27与15和19之间最小的数字交换,也就是与15交换位置。然后再与18和28之间最小的数字交换,即与18交换,再与25交换。

通过这三步,最终得到的就是一个小堆。

2.堆的创建 

下面我们给出一个数组,这个数组逻辑上可以看作一个完全二叉树,当时它还不是一个堆,现在我们通过算法,把他构建成一个堆。可是这个完全二叉树的根节点的左右两个子树也不是堆,如何进行调整呢?只需要从倒数第一个非叶子节点的子树开始调整即可,一直到根节点。 

接下来尝试把下面的数组调整成为一个大堆。 

int a[] = {1,5,3,8,7,6}

 3.堆的实现

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity; 
}Heap;
//堆的初始化
void HeapInit(Heap* hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

堆的实现,我们仍然采用多个文件的方式进行,以上是头文件中的代码,也是我们需要完成的函数。

 在上述的代码中的结构体中size表示数组的大小(存入有效数据的个数),capacity表示容量。

4.堆的初始化和销毁

初始化和销毁比较简单。 

初始化:当这个数组没有使用过的时候,其中容量和大小都未0。也没有动态开辟内存。

销毁:释放掉动态开辟的内存,将容量和大小设置为0。

void HeapInit(Heap* hp) {
	assert(hp);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
}

void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->_a);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
}

5.堆的插入 

 因为在堆的初始化的时候,是将大小和容量均初始化为0的,所以在插入数据的时候,要判断这个堆中的数组是否需要扩容,由于关于扩容的代码不会经常用到,所以这里就不单独封装函数。

5.1扩容

 当size和capacity相等的时候,就代表此时的数组已满,需要扩容。

	if (hp->_capacity == hp->_size) {
		int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;
		HPDataType* tmp = realloc(hp->_a,newcapacity * sizeof(HPDataType));
		if (tmp == NULL) {
			perror("malloc fail");
			exit(1);
		}
		hp->_a = tmp;
		hp->_capacity = newcapacity;
	}

通常情况下扩容都是扩大二倍,当时由于初始化为0了,所以需要判读是否为0,这里采用三目操作符,如果为0,就设置为4,不为0就乘2。

调整动态开辟的内存的大小需要使用realloc函数。

或许有读者疑惑,realloc函数的第一个参数需要填要调整的动态内存块的起始地址。可是在开始的时候hp->_a是NULL。这怎么办呢???

其实realloc函数的第一个参数是NULL的时候,它的功能就和malloc函数的功能一样了,就会直接开辟空间。

第二个参数是内存的大小(单位字节),所以是newcapacity * HPDataType。

最后不要忘记将tmp和newcapacity赋值给hp。

5.2向上调整与向下调整

 将数据拿出来插入到数组的最后一个位置上,可是此时并不能肯定该完全二叉树就是堆。

所以我们需要根据该数据进行调整。

	hp->_a[hp->_size++] = x;
	AdjustUp();

首先将数据放在数组的最后一个位置上。 所以需要对这个数据进行向上调整。

假设此时我们需要得到一个大堆。

5.2.1向上调整
void AdjustUp(HPDataType* a, int child);

 首先我们知道,使用数组在逻辑上构成完全二叉树时。父亲和孩子的下标是有一定规律的。

	//左孩子 = 父亲 * 2 + 1 
	//右孩子 = 父亲 * 2 + 2 

由于是向上调整,我们知道的参数只有孩子的下标,又因为整数除法,很容易我们就可以知道父亲节点的下标。

	int parent = (child - 1) / 2;//整数除法

 如果孩子更大就和父亲交换位置。可是交换位置后仍然可能存在不是大堆的情况,所以需要继续调整,直到是大堆为止。

void AdjustUp(HPDataType* a, int child) { 
	//左孩子 = 父亲 * 2 + 1 
	//右孩子 = 父亲 * 2 + 2 
	int parent = (child - 1) / 2;//整数除法
	while (child > 0) {
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}
}

当child不是0的时候,就代表还可以继续比较,所以结束条件就是child等于0时。或者是parent大于等于0,因为如果parent小于了代表越界了,此时以及不能比较了。

Swap函数是用于交换的。

void Swap(HPDataType* a, HPDataType* b) {
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

6.堆的删除 

// 堆的删除
void HeapPop(Heap* hp);

堆的删除是指删除堆顶(根位置)的数据 。那么该如何删除呢?

假设一下有这样一个数组并且按照小堆的方式存放。

此时需要删除数据1。如果我们采用向前移动覆盖的方式进行删除。那么会得到一个新的堆 

不难发现此时这个堆的关系全乱了,不再是小堆,而且我们无法轻易的进行更改。所以直接向前移动覆盖这个堆顶的数据是行不通的。

那么该如何删除数据呢?

将数组最后一个数据覆盖到第一个位置上就行了。

 

虽然此时的二叉树仍然不是一个小堆,但是只有堆顶的数据关系不正确而且。此时我们就需要用到向下调整算法来使其重新变成一个小堆。 

5.2.2 向下调整

 首先将最后一个数据放在第一个位置上。

//小堆
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(hp->_size > 0);
	hp->_a[0] = hp->_a[hp->_size - 1];
	hp->_size--;
	AdjustDown(hp->_a, hp->_size, 0);
}

然后进行向下调整。

 已知父亲的下标,很轻易可以知道左右两个孩子的下标。然后就是找到两个孩子的最小值与父亲比较。

为了更快速的找到最小的那个孩子的下标,我们可以采用假设法。首先假设左孩子最小。如果右孩子比左孩子小,就可以直接+1,因为右孩子的下标比左孩子刚好大1。

然后就是比较。参考一下代码。

void AdjustDown(HPDataType* a, int size, int parent) {
	int child = parent * 2 + 1;//假设左孩子最小
	while (child < size) {
		if (child + 1 < size && a[child] > a[child + 1]) {
			++child;
		}
		if (a[parent] > a[child]) {
			Swap(&a[parent],&a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			break;
		}
	}
}

 7.取堆顶数据

这个很简单,就不过多讲解。

HPDataType HeapTop(Heap* hp) {
	assert(hp);
	assert(hp->_size > 0);
	return hp->_a[0];
}

只需要保证此时堆不为空即可。

8.堆的数据个数

int HeapSize(Heap* hp) {
	assert(hp);
	return hp->_size;
}

9.判读堆是否为空 

bool HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->_size == 0;
}

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

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

相关文章

动手学Avalonia:基于硅基流动构建一个文生图应用(一)

文生图 文生图&#xff0c;全称“文字生成图像”&#xff08;Text-to-Image&#xff09;&#xff0c;是一种AI技术&#xff0c;能够根据给定的文本描述生成相应的图像。这种技术利用深度学习模型&#xff0c;如生成对抗网络&#xff08;GANs&#xff09;或变换器&#xff08;T…

一个spring boot项目的启动过程分析

1、web.xml 定义入口类 <context-param><param-name>contextConfigLocation</param-name><param-value>com.baosight.ApplicationBoot</param-value> </context-param> 2、主入口类: ApplicationBoot,SpringBoot项目的mian函数 SpringBo…

“删错文件后如何高效挽救?两大恢复策略全解析“

在数字化日益深入生活的今天&#xff0c;数据已成为我们工作、学习和娱乐不可或缺的一部分。然而&#xff0c;删错文件的经历却如同数字世界中的一场“小插曲”&#xff0c;不经意间就可能让我们陷入数据丢失的困境。无论是误触删除键、清空回收站&#xff0c;还是软件故障导致…

第十八章 Express multer 文件上传

本章将学习Express multer 文件上传 &#xff0c;因为Nest 的文件上传是基于 Express 的中间件 multer 实现的&#xff0c;所以在学习 Nest 文件上传之前&#xff0c;我们先学习下 multer 包 首先先创建 multer-test 文件夹执行下面代码 创建package.json npm init -y接着安装…

python学习01-安装

一.安装python 1.下载安装 首先要先下载安装包&#xff1a;Download Python | Python.org 网上建议下载python3.7.4版本&#xff0c;那就下载这个版本 2.验证 二.pip 1.升级pip pip 是 Python 的包管理工具&#xff0c;该工具提供了对Python 包的查找、下载、安装、卸载的…

BigMarket-基础层持久化数据库

需求 工程对接数据库 图例 结构说明 app-主要用于启动&#xff0c;没有业务逻辑 domain-业务逻辑&#xff0c;如积分的兑换&#xff0c;抽奖&#xff0c; infrastructure-基础层&#xff0c;技术支持&#xff0c;数据服务数据持久化&#xff1a;MySQL&#xff0c;redis&am…

在NVIDIA Jetson平台离线部署大模型

在NVIDIA Jetson平台离线部署大模型&#xff0c;开启离线具身智能新纪元。 本项目提供一种将LMDeploy移植到NVIDIA Jetson系列边缘计算卡的方法&#xff0c;并在Jetson计算卡上运行InternLM系列大模型&#xff0c;为离线具身智能提供可能。 最新新闻&#x1f389; [2024/3/1…

2024最适合小白的Midjourney教程,值得收藏!

一、Midjourney 的提示词 1、提示可以包括一个或多个图像 URL、多个文本短语以及一个或多个参数 1&#xff09;Image Prompts&#xff08;图像提示&#xff09;&#xff1a;可以将图像 URL 添加到提示中以影响最终结果的样式和内容。图像 URL 始终出现在提示的前面。文件应以.…

小型内衣裤洗衣机哪个牌子好?五大卓越臻品不容错过!

随着科技的发展&#xff0c;市面上也出现许多便利的小家电。其中被多次讨论起来的莫过于是内衣洗衣机&#xff0c;选择一款耐用、质量优秀的内衣洗衣机&#xff0c;不仅可以减少洗衣负担&#xff0c;还能提供高效的洗涤效果。然而&#xff0c;随着内衣洗衣机的爆火&#xff0c;…

一种低导通损耗的USB 电源开关电路

摘 要&#xff1a;本文设计了一种低导通损耗的USB 电源开关电路。该电路采用自举电荷泵为N 型功率管提供足够高的栅压&#xff0c; 以降低USB 开关的导通损耗。在过载情况下&#xff0c; 过流保护电路能将输出电流限制在0. 3 A。 1 引言 通用串行总线&#xff08; Universal …

windows使用foxglove可视化

1 部署 安装桌面应用 Download - Foxglove 安装完毕后按照要求要注册账号&#xff0c;注册完账号即可正常打开foxglove; 下载ecal-foxglove-bridge.exe&#xff0c;作用是转发ecal话题到foxglove GitHub - eclipse-ecal/ecal-foxglove-bridge: ecal-foxglove-server 2. 可…

人脸表情识别Facial Expression Recognition基于Python3和Keras2(TensorFlow后端)

人脸表情识别项目是一个结合了计算机视觉和深度学习技术的高级应用&#xff0c;主要用于分析和理解人类面部表情所传达的情感状态。这样的系统可以用于多种场景&#xff0c;比如情绪分析、用户交互、市场调研、医疗诊断以及人机接口等领域。 一个典型的人脸表情识别项目可以分…

开源项目的发展趋势之托管平台

引言 回溯至十五年前的求学时光&#xff0c;当我们初涉编程的浩瀚海洋&#xff0c;面对程序设计这一崭新挑战&#xff0c;曾渴望能有一份现成的源码作为启航的灯塔。然而&#xff0c;在那个时代&#xff0c;除了有限的SDK资源外&#xff0c;寻觅到高质量、可借鉴的源代码并非易…

如何给CAD文件加密丨五种超级简单的CAD图纸加密方法

CAD图纸作为企业核心竞争力的体现&#xff0c;其安全性直接关系到企业的生产效率和市场竞争力。一旦图纸被泄露&#xff0c;竞争对手可能会迅速模仿或改进产品&#xff0c;从而抢占市场份额。此外&#xff0c;图纸的非法获取还可能涉及知识产权纠纷&#xff0c;给企业带来法律风…

MES 功能模块

MES系统&#xff08;Manufacturing Execution System&#xff0c;生产执行系统&#xff09;是制造业企业的关键管理系统之一&#xff0c;它通过集成生产计划、工艺流程、物料管理和生产过程数据等&#xff0c;实现了对生产和制造过程的全面管理和监控。MES系统的功能模块主要包…

FPGA-Verilog-Vivado-软件使用

这里写目录标题 1 软件配置2 FPGA-7000使用2.1 运行启动方式 1 软件配置 编辑器绑定为Vscode&#xff0c;粘贴VS code运行文件的目录&#xff0c;后缀参数保持不变&#xff1a; 如&#xff1a; D:/Users/xdwu/AppData/Local/Programs/Microsoft VS Code/Code.exe [file name]…

vscode远程调试python代码

第一步&#xff1a; vscode设置 vscode也支持通过remote的方法连接我们在命令行中发起的debug server。首先我们要配置一下debug的config。 还是点击VSCode侧边栏的“Run and Debug”&#xff08;运行和调试)&#xff0c;单击"create a lauch.json file" 第二步&a…

Java--static详解

1.static静态的意义&#xff0c;加在属性面前就为静态属性&#xff1b;加在方法面前就为静态方法 2.如图&#xff0c;定义了一个静态属性age&#xff0c;一个非静态属性score&#xff1b; 输出语句一共四句&#xff0c;其中第三句报错&#xff0c;由于静态和非静态的区别&…

软考:软件设计师 — 2.操作系统

二. 操作系统 1. 操作系统概念 &#xff08;1&#xff09;操作系统的作用 操作系统是计算机硬件之上的第一层软件系统。 操作系统通常用来&#xff1a; 管理系统的硬件、软件、数据资源。控制程序运行。人机之间的接口。应用软件与硬件之间的接口。 可概括为&#xff1a; …

7.11 cf div3 C

Problem - C - Codeforces 操作 根据给定的索引数组ind和字符串c&#xff0c;按照一定的顺序修改字符串s中对应位置的字符。具体来说&#xff0c;第i次操作会修改s中索引为indi的位置的字符&#xff0c;将其设置为ci。 将c字符串按照从小到大排序&#xff0c;替换ind数组所表…