堆的基本实现

一、堆的概念

在提出堆的概念之前,首先要了解二叉树的基本概念

一颗二叉树是节点的有限集合,该集合:

1、或者为空;

2、或者由一个根节点加上两颗分别称为左子树和右子树的两颗子树构成;

堆就是一颗完全二叉树;

堆有两种:小堆和大堆

堆在内存上的存储是数组形式的(物理结构);我们认为想象成链式结构(逻辑结构)

通过数组结构去实际存储,有这样的规律:每个父节点的下标乘以2加1就是左孩子节点,加2就是右孩子节点;无论是左孩子还是右孩子,其下标减去1再 /2 就是父节点的下标,这就是通过数组存储堆(完全二叉树)的优点。


二、堆实现的相关头文件

cpp 复制代码
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
#include<errno.h>
//堆有两种:大堆、小堆
typedef int HPDataType;

typedef struct Heap
{
	HPDataType* arr;
	int size;
	int capacity;
}Heap;

//堆的构建
void HeapInit(Heap* php);

//堆的销毁
void HeapDestroy(Heap* php);

//向上调整
void AdjustUp(Heap* php, int child);
//堆的插入
void HeapPush(Heap* php, HPDataType x);

//向下调整
void AdjustDown(Heap* php, int parent, int size);
//堆的删除
void HeapPop(Heap* php);

//取出堆顶的数据
HPDataType HeapTop(Heap* php);

//堆的数据个数
int HeapSize(Heap* php);

//堆的判空
bool HeapEmpty(Heap* php);

堆是完全二叉树,所以用数组存储可以方便访问子节点和父节点;


三、堆基本接口的实现(大堆)

1、堆的构建(初始化)

cpp 复制代码
void HeapInit(Heap* php)
{
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

2、堆的销毁

cpp 复制代码
//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	if (php->arr == NULL)
	{
		free(php->arr);
	}
	php->arr = NULL;
	php->size = php->capacity = 0;
}

3、堆的插入

cpp 复制代码
void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc failed");
			exit(1);
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size++] = x;

	//向上调整
	AdjustUp(php->arr,php->size-1);
}

堆只有大堆和小堆之分,插入数据和顺序表一样,关键是插入数据之后要对数组里面的数据进行调整;

插入数据用到向上调整

cpp 复制代码
//向上调整
void AdjustUp(HPDataType* arr,int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		if (arr[child] > arr[parent])
		{
			//交换数组里面的值
			Swap(&arr[child], &arr[parent]);
			//继续比较大小要将child和parent的值向上移动
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			//之前的数据都是一个一个建好的大堆
			//父节点的值比子节点的大,停止
			break;
		}
	}
}
cpp 复制代码
void Swap(HPDataType* x, HPDataType* y)
{
	HPDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

每次插入进堆的数据都是孩子节点(形参名称),向上调整的原因就是每个子树的父亲节点理论上是要大于孩子节点的,但是插入的数据可能要比父节点大,所以在向上调整函数里面要先通过孩子节点的下标找到对应的父节点,之后再比较看是否要交换,直到父亲节点大于子节点;

循环结束的条件是child>0,当child为0的时候说明已经调整到根节点的位置了。

4、堆的删除

cpp 复制代码
//堆的删除
void HeapPop(Heap* php)
{
	assert(php && php->size>0);
	//删除是删除的是堆顶的数据,若是直接让数组整体往前移动 堆被打乱 要重新建堆 时间复杂度高;
	//方法:交换堆首位的数据,让size--,再从堆顶开始向下调整
	Swap(&php->arr[0],& php->arr[php->size - 1]);
	php->size--;
	AdjustDown(php->arr, 0, php->size - 1);
}

堆的删除删除的是根节点的数据,也就是数组里面下标为0的数据;如果直接移动数组下标删除,那么新数组就不再是一个堆,此时要重新建堆,代价过大;

采用这种方式:每次进行删除之前先交换堆首尾的数据,之后再让size--,就访问不到原来的根节点,此时得到的新数组,除了根节点处的数据,它的子树都是大堆;此时进行向下调整算法,把这个不应该是根节点的数据向下调整,把下面的数据里面最大的调整到根节点处;

向下调整算法

cpp 复制代码
void AdjustDown(HPDataType* arr, int parent, int size)//size指向的是最后一个有效数据
{
	int child = parent * 2 + 1;//定义为左孩子
	while (child <= size)
	{
		if (child+1<=size && arr[child] < arr[child + 1])//当最后一个子树只有左孩子时,child+1越界
		{
			child = child + 1;//若是右孩子较大,那么就定义成右孩子
		}//总是大的调到上面去

		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

向下调整是从下标为0处开始调整的,这个0下标位置就是父节点,通过乘以2加上1的办法找到子节点,但是要找到子节点里面较大的那个所以上面代码就有了child处和child+1处数据大小的比较,若是父节点小于子节点就交换;每次调整完都刷新父节点和子节点的下标,以便一直往下面调整直到父节点的数据要大于子节点的数据就停止;

循环结束的条件是child<size,这里的size是数组里面最后一个有效数的下标;

5、堆顶数据、堆的数据个数、堆的判空

cpp 复制代码
//取出堆顶的数据
HPDataType HeapTop(Heap* php)
{
	assert(php && php->size>0);
	return php->arr[0];
}

//堆的数据个数
int HeapSize(Heap* php)
{
	assert(php);
	return php->size;
}

//堆的判空
bool HeapEmpty(Heap* php)
{
	assert(php);
	return php->size == 0;
}
相关推荐
蹉跎x20 分钟前
力扣1358. 包含所有三种字符的子字符串数目
数据结构·算法·leetcode·职场和发展
巫师不要去魔法部乱说1 小时前
PyCharm专项训练4 最小生成树算法
算法·pycharm
IT猿手2 小时前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解GLSMOP1-GLSMOP9及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·算法·机器学习·matlab·强化学习
阿七想学习2 小时前
数据结构《排序》
java·数据结构·学习·算法·排序算法
王老师青少年编程2 小时前
gesp(二级)(12)洛谷:B3955:[GESP202403 二级] 小杨的日字矩阵
c++·算法·矩阵·gesp·csp·信奥赛
Kenneth風车2 小时前
【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)111
算法·机器学习·分类
eternal__day3 小时前
数据结构(哈希表(中)纯概念版)
java·数据结构·算法·哈希算法·推荐算法
APP 肖提莫3 小时前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
OTWOL3 小时前
两道数组有关的OJ练习题
c语言·开发语言·数据结构·c++·算法
qq_433554543 小时前
C++ 面向对象编程:递增重载
开发语言·c++·算法