数据结构->堆的实现详解

目录

一、堆的性质:

二、简易实现堆及要点解析:

1、堆的初始化与销毁:

2、堆的插入与删除(向上与向下调整):

3、返回堆顶元素、堆中元素个数、以及判空:

4、测试用例:


一、堆的性质:

        堆就是以二叉树的顺序存储方式来存储元素,一般是数组数据看做一棵完全二叉树,也就是说堆总是一棵完全二叉树。

        而堆又分为大堆和小堆:

  • 大堆:父节点 >= 其任意子节点

  • 小堆:父节点 <= 其任意子节点

概念图如下:

二、简易实现堆及要点解析:

头文件定义及主要实现的功能:

typedef int HPDateType; // 设置堆中元素的类型,方便进行更改

typedef struct Heap
{
	HPDateType* a;
	int Size;		// 堆中的元素个数
	int Capacity;   // 堆容量大小
}HP;

// 堆的初始化与销毁
void HeapInit(HP* php);
void HeapDesTroy(HP* php);

// 堆的插入与删除
void HeapPush(HP* php, HPDateType x);
void HeapPop(HP* php);

// 返回堆顶元素、堆中元素个数、以及判空
HPDateType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);

// 向上调整与向下调整以及交换
void AdjustUp(HPDateType* a, int child);
void AdjustDown(HPDateType* a, int parent);
void Swap(HPDateType *p1, HPDateType* p2);

1、堆的初始化与销毁:

void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->Capacity = php->Size = 0;
}

void HeapDesTroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->Capacity = php->Size = 0;
}

2、堆的插入与删除(向上与向下调整):

堆的插入:

        在进行堆的插入时我们可以在堆尾进行插入, 然后再将该元素不断的向上进行调整,也就是与它的父节点进行比较,如果(以小堆为例)子节点小于父节点,则进行交换,不断重复该过程直到符合堆的结构为止。(大堆更改交换条件即可)

堆的删除:

        在进行堆的删除时实际上是删除堆顶的元素,如果直接删除堆顶元素再将整个堆进行重构的话那效率将非常低下,再不然也会打乱堆的结构,所以我们可以先将堆顶的元素与堆尾的元素进行交换,然后删除堆尾的元素,再将堆顶元素进行向下调整,也就是与它的子节点进行比较,如果(以小堆为例)父节点大于子节点,则进行交换,不断重复该过程直到符合堆的结构为止。(大堆更改交换条件即可)

void Swap(HPDateType* p1, HPDateType* p2)
{
	HPDateType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

// 向上调整
void AdjustUp(HPDateType* a, int child)// 以小堆为例
{
	int parent = (child - 1) / 2; // 父节点 = (其子节点 -1) / 2
	while (child > 0)
	{
		if (a[child] < a[parent])// 若子节点小于父节点则交换它们的值
		{
			Swap(&a[child], &a[parent]);

			child = parent;// 更新父子节点
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

// 堆的插入
void HeapPush(HP* php, HPDateType x)
{
	assert(php);
	if (php->Size == php->Capacity)
	{
		int NewCapacity = php->Capacity == 0 ? 4 : php->Capacity * 2;
		HPDateType* tmp = (HPDateType*)realloc(php->a, NewCapacity * sizeof(HPDateType));
		if (tmp == NULL)
		{
			perror("realloc error");
			exit(-1);
		}
		php->a = tmp;
		php->Capacity = NewCapacity;
	}
	php->a[php->Size] = x;
	php->Size++;
	AdjustUp(php->a, php->Size - 1);
}

// 向下调整
void AdjustDown(HPDateType* a, int size, int parent)// 以小堆为例
{
	int child = parent / 2 + 1; // 子节点 = 其父节点 * 2 + 1
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] < a[child])// 找两个子节点中小的节点
		{
			child++;
		}
		if (a[child] < a[parent])// 若子节点小于父节点则交换它们的值
		{
			Swap(&a[child], &a[parent]);

			parent = child;// 更新父子节点
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 堆的删除
void HeapPop(HP* php)
{
	assert(php);
	assert(php->Size > 0);

	// 交换首尾并删除尾后向下调整
	Swap(&php->a[0], &php->a[php->Size - 1]);
	php->Size--;

	AdjustDown(php->a, php->Size, 0);
}

3、返回堆顶元素、堆中元素个数、以及判空:

HPDateType HeapTop(HP* php)
{
	assert(php);
	assert(php->Size > 0);
	return php->a[0];
}

size_t HeapSize(HP* php)
{
	assert(php);
	return php->Size;
}

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->Size == 0;
}

4、测试用例:

给定一个无序数组,以小堆存储结构呈现

void test1()
{
	int a[] = { 3,6,8,1,3,2,7,9,5 };
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		HeapPush(&hp, a[i]);
	}

	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}

	HeapDesTroy(&hp);
}

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值