当前位置: 首页 > article >正文

【数据结构】堆(Heap)详解----定义堆、初始化,删除、插入、销毁、判空、取堆顶

文章目录

  • 一、堆的概念及其性质:
    • 堆的概念:
    • 堆的性质:
  • 二、堆的定义及其基础操作的代码实现(C语言版)
    • 1.定义堆
    • 2.堆的初始化
    • 3.堆的销毁
    • 4.堆的插入
    • 5.堆的删除
    • 6.取堆顶的数据
    • 7.堆的数据个数
    • 8.堆的判空
  • 总结:


提示:以下是本篇文章正文内容

一、堆的概念及其性质:

堆的概念:

:是一种特殊的完全二叉树,使用数组进行存储,其所有的父结点大于等于或者小于等于它们的子结点,所以堆分为大根堆和小根堆。
大根堆(最大堆)::所有的父结点大于等于它们的子结点,堆顶是最大值。
小根堆(最小堆)::所有的父结点小于等于它们的子结点,堆顶是最小值。
举例
在这里插入图片描述
在这里插入图片描述

堆的性质:

对于具有n个结点的完全二叉树,若按照从上至下,丛左至右的数组顺序对所有结点从0开始编号,则对于序号为i的节点:
1.若i=0,i为根节点编号,无双亲结点。
2.若i>0,i位置结点的双亲序号:(i-1)/2;
3.若2i+1<n,左孩子序号:2i+1。
4.若21+2<n,右孩子序号:2i+2。

二、堆的定义及其基础操作的代码实现(C语言版)

因为堆的定义以及销毁、初始化很简单,就不赘述啦,直接看代码。难点主要在于堆的插入和删除,我们会用到两种方法向上调整法向下调整法,我会详细讲述。

1.定义堆

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _arr;
	int _size;//有效数据个数
	int _capacity;//空间大小
}Heap;

2.堆的初始化

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

3.堆的销毁

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

4.堆的插入

对于堆的插入我们可以分两种情况:
1.插入的结点是根结点,那么就直接hp->_arr[hp->_size] = x就行。
2.若插入的不是根节点,那么我们不仅要hp->_arr[hp->_size] = x,还需要进行向上调整
向上调整法(以小根堆为例,掌握啦小根堆大根堆也就会啦~):
其思想主要是将元素直接插入到最后一个位置,若不是根结点,则需要与其父结点进行比较,若比父结点小,则与父节点进行交换,一直重复该操作,直到孩子结点大于等于父结点或者到根结点。
为了方便柚柚们理解,我来为柚柚们讲一个例子:

6 5 4 3为例
第一次插入6的时候,为根节点,此时直接插入作为根结点,不需要调整,_size++。

第二次插入5的时候,此时5有父结点,其父节点序号为(1-1)/2 = 0,即为6,发现孩子结点比父结点小,我们就要把它们交换,交换之后让孩子结点指向序号为0的结点,此时为根节点不需要调整,插入完成,_size++。
在这里插入图片描述
第三次插入4,此时4有父结点,其父节点序号为(2-1)/2 = 0,即为5,发现孩子结点比父结点小,所以我们要把它们交换,交换之后让孩子结点指向序号为0的结点,此时为根节点不需要调整,插入完成,_size++。
在这里插入图片描述
第四次插入3,此时3有父结点,其父节点为(3-1)/2 = 1,即为6,发现孩子结点比父结点小,所以我们要把它们交换,交换之后让孩子结点指向序号为1的结点,然后再让1这个位置的结点与其父结点比较,其父结点为(1-1)/2 = 0,即为4,发现孩子结点比父结点大,我们就不需要再进行调整,插入完成,_size++。
在这里插入图片描述
整个过程就是这样,其实是很好理解,看完图应该就能够理解啦~

代码实现:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
//向上调整法
void AdjustUp(HPDataType* arr, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)//不需要等于,child只要走到根节点的位置,根节点没有父节点不需要交换
	{
		if (arr[child] < arr[parent])//若孩子结点比父结点小则交换
		{
			Swap(&arr[parent], &arr[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);

	if (hp->_capacity == hp->_size)
	{
		int newCapacity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity;//分配空间,因为刚开始容量为0,所以要特殊处理
		HPDataType* tmp = (HPDataType*)realloc(hp->_arr,newCapacity * sizeof(HPDataType));
		if (tmp == NULL)  exit(1);//分配内存失败,结束程序
		hp->_arr = tmp;
		hp->_capacity = newCapacity;
	}
	hp->_arr[hp->_size] = x;

	AdjustUp(hp->_arr, hp->_size);//向上调整法
	
	hp->_size++;
}

5.堆的删除

对于堆的删除我们需要注意的是我们删除的是根结点,为了方便删除根节点,我们将根结点与最后一个结点进行交换,然后_size–,就把根节点删啦,但是此时打乱了堆的顺序,根结点可能就不是最小的啦,所以我们要对根节点进行向下调整。具体步骤就是,让交换之后的根结点与它的孩子结点比较,若比孩子结点大,则与孩子结点交换,然后让父节点指向该孩子结点,直到孩子结点的下标大于等于n(结点的个数)或者比其孩子结点都小;若比孩子结点们小,则结束,删除完成,其实理解了向上调整,向下调整就很容易理解啦,所以我就不再举例子啦。

//向下调整法
void AdjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;//左孩子

	while (child < n)
	{
		//找左右孩子中找最小的
		if (child + 1 < n && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;

		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* php)
{
	assert(php && php->_size);

	Swap(&php->_arr[0], &php->_arr[php->_size - 1]);

	--php->_size;

	AdjustDown(php->_arr, 0, php->_size);
}

6.取堆顶的数据

HPDataType HeapTop(Heap* php)
{
	assert(php && php->_size);

	return php->_arr[0];
}

7.堆的数据个数

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

8.堆的判空

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

总结:

若是还没有理解向上调整法和向下调整法,柚柚们可以多看看代码多画画图,还有就是自己搞几个样例跟着程序调试,就可以搞懂啦~,日后还会更新堆排还有其它数据结构的知识,感兴趣的柚柚们可以三连,关注博主喔!


http://www.kler.cn/news/330111.html

相关文章:

  • 试用Foxit PDF: 在网页中单页展示PDF
  • 计算机网络期末复习真题(附真题答案)
  • 构建.NET Core Web API为Windows服务安装包
  • 配置Scrapy项目
  • 3、AI测试辅助-测试计划编写(自动生成任务甘特图)
  • [C#]C# winform部署yolov11目标检测的onnx模型
  • 计算机毕业设计 基于Python的音乐平台的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档
  • 每天五分钟深度学习PyTorch:如何使用GPU来跑深度学习算法模型?
  • 力扣(leetcode)每日一题 2516 每种字符至少取 K 个 | 滑动窗口
  • ESP32简介
  • 我博客网站又遭受CC攻击了,记录一下
  • JAVAIDEA初始工程的创建
  • cMake学习笔记(初级使用)
  • SpringBoot开发——Spring Security中获取当前登录用户信息的方式
  • 初识chatgpt
  • C++ 矩阵拼接相关问题记录
  • 在Linux中创建检查点并还原的工具——criu
  • AIGC(AI网站分享)
  • 开源模型应用落地-模型微调-语料采集-数据格式化(四)
  • mybatis如何与spring的结合