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

数据结构 【堆实现】

        上文提到堆是一种特殊的二叉树,其中它的父结点均不大于或者不小于其子结点的值。堆总是一棵完全二叉树。其中,堆的父节点全部不小于它的子结点时称为大堆,堆的父结点全部不大于其子结点的堆称为小堆。

        堆可以由两种结构来实现,分别是顺序结构和链式结构。这里我们先来实现堆的顺序结构。

        堆中各结点的父节点与子节点在数组中存放的位置有下面的关系:

         下面,我们就来一起实现一下堆这个数据结构。

1、准备工作

        这里我们要实现的是顺序结构存储堆,所以底层为数组,也就是顺序表的形式。先定义顺序表的结构。

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

        定义好顺序表的结构之后就需要对创建堆并对其进行初始化了。

2、堆的初始化 

        这里使用接收结构体指针的方式来进行实现,先创建一个堆对象,传入这个对象的地址。使用malloc为数组开辟空间,并将size和capacity赋初值。

void HeapInit(HP* php)
{
	assert(php);

	HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * 4);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->a = tmp;
	php->size = 0;
	php->capacity = 4;
}

3、堆的数据插入

        这里实际上就是在数组中插入一个数据,但是与前面的顺序表插入数据不同之处在于,堆在插入数据之后需要进行调整,我们这里以大堆为例。再插入数据之后假设插入的数据大于它的父结点,那么就需要和它的父结点交换位置。逻辑上是这样的:

        在这部分称之为向上调整。向上调整过程中,假设子结点的数据大于父结点的数据就进行交换位置。

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

void AdjustUp(HPDataType* a,int child)
{
	int parent = (child - 1) / 2;
	while (parent >= 0)//parent<0就停止
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;//进行迭代
		}
		else 
		{
			break;//a[child] < a[parent]跳出
		}
	}
}


void HeapPush(HP* php, HPDataType val)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2; 
	}

	php->a[php->size] = val;
	php->size++;
	AdjustUp(php->a,php->size-1);//向上调整

}

3、堆顶的删除

        在堆的删除这里,我们需要删除的是堆顶元素。我们知道,对于数组来说删除数组的第一个元素并不是很容易。这里我们使用一个新的思路:将堆顶元素与最后一个元素进行交换位置,再删除掉数组末尾位置的元素。

        上面的思路就可以写成下面的形式:

void AdjustDown(HPDataType* a, int parent,int size)
{
	int maxChild = 0;
	
	while (parent <= size)
	{
		int leftChild = parent * 2 + 1;
		int rightChild = parent * 2 + 2;
		maxChild = leftChild;//找到最大的子结点进行交换
		if (a[rightChild] > a[leftChild])
		{
			maxChild = rightChild;
		}

		if (maxChild <= size && a[parent] < a[maxChild])//防止越界访问
		{
			Swap(&a[parent], &a[maxChild]);
			parent = maxChild;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(HP* php)
{
	assert(php);
	 
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, 0,php->size);
}


http://www.kler.cn/a/412622.html

相关文章:

  • 前端性能优化之任务管理/调度
  • vue3typescript,shims-vue.d.ts中declare module的vue声明
  • MySQL系列之远程管理(安全)
  • 【漏洞复现】CVE-2020-13925
  • Spring |(五)IoC/DI的注解开发
  • Linux—进程概念学习-03
  • 力扣876. 链表的中间结点
  • nginx和netcore加载常见的3D模型
  • Go 中的并发 Map:深入探索 sync.Map 及其他实现方法
  • Django中 model 一对一 一对多 多对多关系 关联
  • NR 5G SIB1读取失败应该怎么办?
  • Ubuntu系统通过命令行连接WiFi
  • 美创科技获选“金智奖”年度创新解决方案,为工业企业数据安全治理提供思路
  • 图书系统小案例
  • 欢迪迈手机商城:基于SpringBoot的用户体验提升
  • JavaWeb三层架构
  • Flutter 开发环境—Linux
  • RabblitMQ 消息队列组件与 libev事件驱动库
  • 【Petri网导论学习笔记】Petri网导论入门学习(十一) —— 3.3 变迁发生序列与Petri网语言
  • Leecode刷题C语言之交替组②
  • 鸿蒙面试 --- 性能优化(精简版)
  • K8s调度器扩展(scheduler)
  • 小程序-基于java+SpringBoot+Vue的微信小程序养老院系统设计与实现
  • C语言中使用动态内存
  • SpringBoot集成minio,并实现文件上传
  • Flutter:封装发送验证码组件,注册页使用获取验证码并传递控制器和验证码类型