【数据结构】堆的向上调整和向下调整以及相关方法

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤

📃

文章目录

一、堆的概念

堆(Heap)是计算机科学中一类特殊的数据结构的统称。如果有一个关键码的集合K = { , , ,..., },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1, 2...,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。除了最后一层以外上面的节点但是非空的,最后一层节点是从左到右依次排布的)

二、堆的性质

🔸 非线性,完全二叉树。适合用数组存储。

🔸堆是无序的,也就是左右可以互换

🔸最值总在 0 号位

根据这个特点我们就可以做很多事情,比如TopK问题 (在一堆数据里面找到前 K 个最大 / 最小的数).

比如点餐软件中有上千家店铺,我想选出该地区好评最多的十家川菜店,我们不用对所有数据排序,只需要取出前 K 个最大 / 最小数据。使用堆排序效率也更高。

三、堆的分类

1.大根堆 2.小根堆

1.大根堆

定义:树中的任意一个双亲节点都大于等于孩子节点。

2.小根堆

定义:树中的任意一个双亲节点都小于等于孩子节点。

四、说明

以下的方法均以小堆来推理,如果想实现大堆,则修改【>】符号等方式实现。

五、堆的结构

c 复制代码
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

🚩六、堆的向上调整

向上调整的前提是,调整位置之前必须是堆。

1.图示

2.代码实现

c 复制代码
//向上调整
void AdjustUp(HPDataType* a, int child)
{
	//传入数组,child为孩子节点下标
	int parent = (child - 1) / 2;
	//当一直交换到根,停止
	while (child>0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			return;
	}
}

⌚️3.时间复杂度分析

时间复杂度:N*logN

最坏情况:调整到根;

最好情况:不用调整,

📌七、堆的向下调整

向下调整的前提是,左右子树必须是小堆或者大堆。

1.思路:

2.代码实现

c 复制代码
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	//一直交换到数的最后,也就是数组的最后一个位置
	while (parent<n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			// 继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			return;
		}
	}
}

八、删除根

1.思路:

先将根与最后一个节点交换,删除最后一个节点,在进行向下调整。

2.代码实现

c 复制代码
void HeapPop(HP* p)
{
	assert(p);
	assert(p->size > 0);

	Swap(&p->a[0], &p->a[p->size - 1]);
	--p->size;

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

九、创建小堆

由于我的AdjustUp函数是用来调整小堆的,所以,这里创建的也是小堆。

1.思路:

传入参数

a:数组,n:是数组元素个数

1.为p->a开辟n个空间;

2.利用memcpy函数,把数组a复制到p->a中

3.在使用AdjustUp调整

2.代码实现

c 复制代码
//建立小堆
void HeapInitArray(HP* p, int* a, int n)
{
	//a:数组,n:是数组元素个数
	assert(p);
	assert(a);

	p->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (p->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	p->size = n;
	p->capacity = n;
	//把传入数组a复制到p->a中
	memcpy(p->a, a, sizeof(HPDataType) * n);

	// 向上调整,调整成一个小堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(p->a, i);
	}
}

十、所有方法实现汇总

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

//初始化
void HeapInit(HP* p)
{
	assert(p);
	p->a = NULL;
	p->size = 0;
	p->capacity = 0;
}

//销毁
void HeapDestroy(HP* p)
{
	assert(p);
	free(p->a);
	p->a = NULL;
	p->size = p->capacity = 0;
}

//插入数据
void HeapPush(HP* p, HPDataType x)
{
	//从最后一个位置插入
	assert(p);
	//扩容
	if (p->capacity == p->size)
	{
		//如果刚开始数组为空,就开辟4个空间。如果不为空,以后每次扩大2倍。
		int newcapacity = p->capacity==0 ? 4 : p->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(p->a, sizeof(HPDataType) * p->capacity);
		if (tmp == NULL)
		{
			perror("realloc fial\n");
			exit(-1);
		}
		p->a = tmp;
		p->capacity = newcapacity;
	}
	p->a[p->size] = x;
	p->size++;
}

//交换
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整
void AdjustUp(HPDataType* a, int child)
{
	//传入数组,child为孩子节点下标
	int parent = (child - 1) / 2;
	//当一直交换到根,停止
	while (child>0)
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			return;
	}
}

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	//一直交换到数的最后,也就是数组的最后一个位置
	while (parent<n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			// 继续往下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			return;
		}
	}
}


//打印二叉树
void HeapPrint(HP* php)
{
	assert(php);

	for (size_t i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}


//建立小堆
void HeapInitArray(HP* p, int* a, int n)
{
	//a:数组,n:是数组元素个数
	assert(p);
	assert(a);

	p->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (p->a == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	p->size = n;
	p->capacity = n;
	//把传入数组a复制到p->a中
	memcpy(p->a, a, sizeof(HPDataType) * n);

	// 向上调整,调整成一个小堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(p->a, i);
	}
}

//删除根
void HeapPop(HP* p)
{
	assert(p);
	assert(p->size > 0);

	Swap(&p->a[0], &p->a[p->size - 1]);
	--p->size;

	AdjustDown(p->a, p->size, 0);
}
//获取根
HPDataType HeapTop(HP* p)
{
	assert(p);
	assert(p->size > 0);

	return p->a[0];
}

//判空
bool HeapEmpty(HP* p)
{
	assert(p);

	return p->size == 0;
}
相关推荐
肥猪猪爸25 分钟前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
linux_carlos26 分钟前
环形缓冲区
数据结构
readmancynn37 分钟前
二分基本实现
数据结构·算法
Bucai_不才41 分钟前
【数据结构】树——链式存储二叉树的基础
数据结构·二叉树
盼海1 小时前
排序算法(四)--快速排序
数据结构·算法·排序算法
一直学习永不止步1 小时前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
珹洺2 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
几窗花鸢2 小时前
力扣面试经典 150(下)
数据结构·c++·算法·leetcode
.Cnn2 小时前
用邻接矩阵实现图的深度优先遍历
c语言·数据结构·算法·深度优先·图论
2401_858286113 小时前
101.【C语言】数据结构之二叉树的堆实现(顺序结构) 下
c语言·开发语言·数据结构·算法·