堆来咯!!!

堆是什么?

是土堆吗?

那当然不是啦~

堆是一种被看作完全二叉树的数组。

那么什么是完全二叉树呢?

如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树。

堆的特点:对于这个树的任意节点来说,满足根节点大于左右子树的值(大堆),或者任意一节点满足根节点的值小于左右子树的值(小堆)

那么来看一下堆是如何?

是不是很好理解?其实本质就是一个装着数据的数组,但我们将它想象成一个树,

 一棵完全二叉树。

而且这棵二叉树是一个小根堆,即父亲小于或等于它的左子树和右子树的值

那么我们就来实现这个堆的基本操作吧

堆的基本操作

1.定义堆的结构体

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* data;
	int size;//堆当前的有效元素个数
	int capacity;//堆的容量
}Heap;

由于堆是一个数组,所以我们需要用顺序表的形式动态的管理它。所以在这个结构体里头我们定义了该结构体数据类型的数组用来存储数据,size用于展示当前堆的有效元素个数,capacity用于展示当前堆的容量。

2.堆的初始化

Heap* HeapInit()
{
	Heap* hp = (Heap*)malloc(sizeof(Heap));
	if (hp == NULL)
	{
		perror("malloc fail");
		return;
	}
	HPDataType* data = (HPDataType*)malloc(sizeof(HPDataType) * 2);
	if (data == NULL)
	{
		perror("malloc fail");
		return;
	}
	hp->data = data;
	hp->capacity = 2;
	hp->size = 0;
	return hp;
}

在堆区建立一个堆的结构体,让里面的数组大小设置为2(纯属个人习惯),两次malloc可能都会弄成NULL,所以一定要记得用if检查一下,之后perror一下malloc的错误,如果创建成功,那么将该结构体的数据依次设置完毕,返回该结构体的指针。

3.堆的销毁

void HeapDestory(Heap* hp)
{
	assert(hp);

	free(hp->data);
	hp->capacity = 0;
	hp->size = 0;
	free(hp);
	hp = NULL;
}

这个还是比较简单的,只需要先将堆的结构体里面的data释放,再将该结构体释放即可,当然两个顺序千万别反过来。free了之后记得养成好习惯将指针都置为NULL

4.堆的向下调整

这是堆的基本操作中最重要的操作之一,那么我们先来看一下向下调整的相关规则:

ex:以小根堆为例

1.先设置当前结点为根结点,之后通过根节点找到左右子树结点,之后比较左右子树的值,通过比较两个值,选出的那个数值较小的值的结点作为child。

2.再比较根结点(父母结点)和那个数值较小的child结点的值,如果child的结点比parent的结点的值更小,那么两个结点进行交换。

3.如果child结点比parent结点的值更大,那么已符合小根堆的规则,那么我们就会调整结束。

4.处理完这个结点后,如果符合第2条规律,就继续以该child结点进行循环操作,直到出现child < n或者符合第3条规则。

那么就来看我们的代码叭~

void AdjustDown(HPDataType* data, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//找出最小/大的那个孩子,先默认最小/大为左孩子,而且,child+1小于n
		if (child + 1 < n && data[child] > data[child + 1])
		{
			child++;
		}
		if (data[child] < data[parent])
		{
			Swap(&data[child], &data[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

可能有人发现了,为什么我的左右孩子节点能够通过parent * 2 + 1或parent * 2 + 2获得,那是因为堆是一棵完全二叉树,又因为根节点初始的下标是0,那么通过对该特点的分析,我们就可以得出:

1.childleft = parent * 2 + 1, childright = parent * 2 + 2;

2.parent = (child - 1) / 2

5.堆的向上调整

我们还是来看堆的向上调整的相关规则:

1.先设置当前结点为孩子结点,通过上述的相关结论,我们通过孩子找到了它的父母结点。

2.比较父母结点的值和孩子结点的值,如果孩子结点的值比较小,那么我们就交换两个结点。

3.如果孩子结点的值比较大,那么我们就调整结束

4.如果符合第2条规则,我们会将parent作为新的孩子继续循环,直到child > 0或者符合第3条规则

void AdjustUp(HPDataType* data, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		//我默认为小根堆
		if (data[child] < data[parent])
		{
			Swap(&data[child], &data[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

6.获取堆顶元素的值

直接来看代码:

HPDataType HeapTop(HP* php)
{
	assert(php);

	return (php->data[0]);
}

这就没什么好说的了,数组嘛,直接下标访问即可

7.判断堆是否为空

bool HeapEmpty(HP* php)
{
	assert(php);

	return (php->size) == 0;
}

只需要判断这个堆的有效元素个数是否为0即可

8.堆的有效元素个数

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

9.入堆操作

展示代码之前,先提几个问题:

1.一个元素从堆这个数组的尾部直接入堆,这个数组是否还是一个大根堆或一个小根堆呢?

很显然,这并不一定是一个大根堆或小根堆。

2.那么如果从堆的头部入堆,是否能是一个大根堆或是一个小根堆呢?

很显然,这比第一点还要离谱一些,不仅仅只是大根堆和小根堆的问题了,如果从头部头插入堆,首先就是移动后面的数据就很麻烦,如果数据量大,那要移动到猴年马月?而且,如果头插入堆,会直接把堆原本的父子结构给破坏了,那这样就得不偿失了。

所以我们一定是只能先从尾部进行入堆之后再进行其他的操作,让整个数组重新变成一个堆。

那么我们怎么才能让这个数组重新变成一个堆呢?

我们来看一下下面的一组图:

 我们通过这个图也就可以得出插入数据的完整过程了

1.先将想要入堆的数据插入整个数组的最尾部

2.之后通过向上调整,将该数据调整到合适的位置即可,这就完成了堆的插入数据,这样还能让这个数组保持堆的性质,不会破坏整个堆的父子关系结构。

那么就来看代码叭!

void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	//push之前判断堆满了没
	if (php->capacity == php->size)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->data, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->capacity = php->capacity * 2;
		php->data = tmp;
	}
	php->data[php->size] = x;
	(php->size)++;

	AdjustUp(php->data, php->size - 1);
}

当然啊,一定要判断这个堆是否满了,满了一定要先realloc一下这个数组,让我们的数组能够正常地存入数组当中,将数组的有效元素个数自增1。

10.出堆顶数据操作

还是先来问一个问题,直接让数组的第一个元素出堆能不能直接解决问题?

是的,不能,我们不能直接删除,如果删了之后,我们需要一个个元素移动,而且我们上面已经提到过了,如果移动整个数组,会影响我们的堆的父子关系的结构,所以我们不能直接删除。

那么我们应该如何删除呢?

来看下面的图:

这图直接就能让我有了思路:

1.先将数组的首元素和数组的尾元素调换位置

2.之后将size--,我们就可以直接删除这个元素了。

3.我们再让堆顶的元素向下调整,就能让我们的数组重新变成一个堆。

嗯,完美!

直接来看代码叭!

void HeapPop(HP* php)
{
	assert(php);
	//删除数据的时候要判断堆是否为空
	assert(!HeapEmpty(php));

	Swap(&(php->data[0]), &(php->data[php->size - 1]));
	(php->size)--;
	AdjustDown(php->data, php->size, 0);
}

当然,还是要判断我们的堆是否为空,空了的话,我们就删了个寂寞了。

那么,这就是整个堆了啦!

那么堆排序到后面的排序算法的时候再将,拜拜~

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

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

相关文章

中国版的ChatGPT,你最看好谁?

一、百度&#xff1a;文心一言升级中&#xff0c;未来支持开源 3月16日&#xff0c;百度正式推出国内首款生成式AI产品“文心一言”&#xff0c;可支持文学创作、文案创作、数理推算、多模态生成等功能。 “文心一言”基于全栈自研的AI基础设施进行学习和训练&#xff1a; ①…

selenium自动化测试面试题【含答案】

目录 1、selenium中如何判断元素是否存在&#xff1f; 2、selenium中hidden或者是display &#xff1d; none的元素是否可以定位到&#xff1f; 3、selenium中如何保证操作元素的成功率&#xff1f;也就是说如何保证我点击的元素一定是可以点击的&#xff1f; 4、如何提高s…

redis持久化方式区别

Redis是一种高级key-value数据库。它跟memcached类似&#xff0c;不过数据可以持久化&#xff0c;而且支持的数据类型很丰富。有字符串&#xff0c;链表&#xff0c;集 合和有序集合。支持在服务器端计算集合的并&#xff0c;交和补集(difference)等&#xff0c;还支持多种排序…

springboot实现修改用户信息功能

目录 1、UserEntity层 2、UserMapper层 3、UserService层 4、UserController类 5、Postman测试 要实现修改用户信息的功能&#xff0c;需要编写对应的代码&#xff1a; 如&#xff1a; 在UserEntity中定义用户实体类的属性。 在UserMapper中编写修改用户的SQL语句&#…

极光笔记 | 如何在Shopify中使用EngageLab (下)

Sendgird发布的《2022 Global Messaging Engagement Report》中揭示了世界各地的用户更喜欢用哪种方式与品牌互动&#xff0c;结论是&#xff1a;“电子邮件仍然是第一名&#xff08;短信紧随其后&#xff09;”。4800多名受访者中&#xff0c;有18%的人将电子邮件列为他们最常…

批处理文件名(不含空格的文件名、以及含空格的文件名)

目的&#xff1a; 整理为&#xff1a; 步骤&#xff1a; 结果生成1.txt内容为&#xff1a; 新建excel 复制1.txt中待处理的文件名字 然后 一直点下一步直到完成&#xff0c;再删除多余列&#xff0c;只剩下名字列 设置P1单元格为1.jpg,下拉递增 设置P1单元格 “REN “&…

Html5代码实现动态时钟

以下是一个简单的HTML5动态时钟的示例&#xff1a; <!DOCTYPE html> <html> <head><title>HTML5动态时钟</title><style>body {margin: 0;padding: 0;background-color: #000;color: #fff;font-size: 5em;font-family: Arial, sans-serif…

1、Windows下编译并搭建AzerothCore服务端

目录前言一、AzerothCore下载二、mysql安装三、boost安装四、OpenSSL安装五、CMake下载六、CMake编译1 - CMake生成vs项目2 - vs项目设置3 - 生成解决方案4 - 安装AzerothCore5 - 添加账号6 - 修改服务器名称7 - 修改客户端的服务器地址前言 客户端对应版本&#xff1a;魔兽世…

STM32 PWM模式与输出比较模式的区别。PWM占空比不生效,在STM32CubeMX中配置PWM的两种模式——蓝桥杯嵌入式

&#x1f38a;【蓝桥杯嵌入式】专题正在持续更新中&#xff0c;原理图解析✨&#xff0c;各模块分析✨以及历年真题讲解✨都已更新完毕&#xff0c;欢迎大家前往订阅本专题&#x1f38f; &#x1f38f;【蓝桥杯嵌入式】蓝桥杯第十届省赛真题 &#x1f38f;【蓝桥杯嵌入式】蓝桥…

chatgpt入口-chatgpt哪里下载

如何开通ChatGPT权限 开通 ChatGPT 的权限与具体的服务提供商有关&#xff0c;不同的服务提供商可能有不同的服务要求和使用条件。下面是一些可以开通 ChatGPT 权限的一般步骤&#xff1a; 寻找服务提供商&#xff1a;首先需要寻找一个可供使用的 ChatGPT 服务提供商。您可以在…

Html5版贪吃蛇游戏制作(经典玩法)

回味经典小游戏&#xff0c;用Html5做了个贪吃蛇的小游戏&#xff0c;完成了核心经典玩法的功能。 游戏可以通过电脑的键盘“方向键”控制&#xff0c;也可以点击屏幕中的按钮进行控制。&#xff08;支持移动端哈&#xff09; 点击这里试玩 蛇的移动是在18 x 18的格子中进行移…

(函数指针) 指向函数的指针

函数指针- 指向函数的指针函数指针的声明和使用通过函数指针调用函数函数指针做参数函数指针数组函数指针的声明和使用 函数指针的声明格式&#xff1a; 返回值类型 (*函数指针名)(参数列表); 其中&#xff1a; *函数指针名 表示函数指针的名称返回值类型 则表示该指针所指向…

swf怎么转换成mp4格式,5个方法都很简单

swf怎么转换成mp4格式&#xff1f;各位小伙伴们有没有遇到过想要打开swf文件却需要安装flash插件的情况呢&#xff1f;其实&#xff0c;swf文件是flash软件或者animate软件导出的flash软件的专属格式&#xff0c;主要应用于动画设计领域,在网页html设计中非常常见&#xff0c;但…

(数字图像处理MATLAB+Python)第四章图像正交变换-第二节:离散余弦变换和K-L变换

文章目录一&#xff1a;离散余弦变换&#xff08;Discrete Cosine Transform&#xff0c;DCT&#xff09;&#xff08;1&#xff09;一维DCTA&#xff1a;定义B&#xff1a;实例&#xff08;2&#xff09;二维DCTA&#xff1a;定义B&#xff1a;实例C&#xff1a;程序&#xff…

UE4 几种常见的项目优化方式

1.灯光范围优化 当屏幕某一块像素被多盏灯光所影响&#xff0c;那么也会拖慢帧率&#xff0c;可以打开灯光复杂度视图进行查看&#xff0c;屏幕上越红的地方灯光复杂度越高&#xff0c;尝试降低灯光半径可以解决&#xff1a; 2.材质纹素优化 有时候我们并不知道目标模型的…

OJ练习LeetCode-118:杨辉三角

题目 118. 杨辉三角 - 力扣&#xff08;LeetCode&#xff09; 可以看到&#xff0c;默认的代码块中出现List<List<Integer>>为二级ArrayList&#xff0c;类似于数组中的二维数组。List<元素类型> 在List<List<Integer>>中&#xff0c;元素类型…

百度天工AIoT设备应用使能平台助力企业低成本开发

数字中国建设的顶层文件《数字中国建设整体布局规划》&#xff08;以下简称《规划》&#xff09;于近日印发&#xff0c;作为数字中国建设的重要基础&#xff0c;《规划》指出&#xff0c;要全面赋能经济社会发展&#xff0c;推动数字技术和实体经济的深度融合&#xff0c;产业…

如何将本地项目上传到Github的方法步骤

默认&#xff1a;已经安装好了git。 第一步&#xff1a;我们需要先创建一个本地的版本库&#xff08;其实也就是一个文件夹&#xff09;。 你可以直接右击新建文件夹&#xff0c;也可以右击打开Git bash命令行窗口通过命令来创建。 第二步&#xff1a;通过命令git init把这个…

chatGPT所在地区不支持怎么解决-需要下载ChatGPT吗

ChatGPT国内能下载吗 ChatGPT是基于云端的人工智能交互服务&#xff0c;无需下载即可使用。因此&#xff0c;您不需要在国内下载ChatGPT&#xff0c;只需要在网络环境联通的情况下&#xff0c;通过浏览器访问ChatGPT官网或合作伙伴提供的ChatGPT服务即可使用。当然&#xff0c…

怎么评价2023年第十三届MathorCup高校数学建模挑战赛?

文章目录赛题思路选题建议1 竞赛信息2 竞赛时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; 选题建议 首先要注意&#xff0c;A、B题为研究生组可选题目&#xff0c;A…
最新文章