【数据结构】结构实现:顺序存储模式实现堆的相关操作

2023-09-16 18:20:07

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:数据结构

🔥该文章着重讲解了使用顺序结构实现堆的插入和删除等操作。

🌍二叉树的顺序结构

 二叉树的顺序存储是指将二叉树中的所有节点按照一定的顺序(一层一层)存储到一个数组中。

 我们可以通过数组下标来表示节点之间的父子关系。

找左孩子节点:leftchild = parent * 2 + 1
找右孩子节点:rightchild = parent * 2 + 2

例如,找B的左孩子 : B的下标 * 2 + 1,得到3 ,即为D。

找父亲节点:parent = ( child -1 )/ 2

例如,找G的父母:(G的下标-1)/ 2 得到 2 ,即为C 。

 需要注意的是,二叉树的顺序存储适用于满二叉树或完全二叉树的情况,对于其他类型的二叉树,顺序存储可能会造成空间浪费访问效率低下的问题。

例如:

 这类二叉树不适合顺序存储,适合链式存储。

🔭 堆

 数据结构中还衍生出了一个结构 —— 堆 , 堆是非线性结构,也是一种完全二叉树。堆的两个常见类型是大堆和小堆。在大堆中,父节点的值总是大于或等于其子节点的值;而在小堆中,父节点的值总是小于或等于其子节点的值。堆通常用数组来实现。
 所以,对于任意一个数组是可以看作一颗完全二叉树,但不一定是堆。


🌏 代码实现

 这里将实现堆的插入和删除,以小堆为例。
 堆的结构特点是:存储结构——数组,逻辑结构——完全二叉树。所以可以定义结构体为:

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

✉️ 堆的插入

 插入的需求为:无论如何插入,都必须保持为堆。因为存储结构是数组,所以选择效率更快的尾插,然后再进行调整(插入的数据会影响它的祖先)。
 调整部分有这样的3种场景:

  1. 不会影响祖先


2.影响部分,但不影响到根。


3.影响到全部祖先


注:这种调整是向上调整。时间复杂度为 O(logN)

💫调整的条件:
在这里插入图片描述

📙实现代码:

//交换数据
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 (child > 0)
	{
		if(a[parent] > a[child])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//插入数据
void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}
	php->a[php->size] = x;
	php->size++;
    //向上调整
	AdjustUp(php->a, php->size - 1);
}

✉️ 堆的删除

 在堆中,删除栈顶元素才是有意义的,这样经过调整后,根就是次小或次大的值。由于堆的存储结构是数组,尾插尾删的效率很高,所以可以考虑将根和最后一个数组元素交换,然后不断调整。


这样的操作之后,可以发现一个特性:左右子树依旧是小堆。



注:这种调整方式为向下调整,时间复杂度为O(logN)。

💫调整条件:
 当子节点的下标超出数组范围时,说明已经没有子节点了,已经换到了叶子。(针对实现的代码而言,是已经没有了左孩子,因为堆是完全二叉树,自然也就没有右孩子,说明换到了叶子)

📙实现代码:

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;//假设左孩子最小
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child])//注意判断child+1是否越界
		{
			//修正
			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);
}

✉️ 其他部分

一些简单的接口:

//初始化
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}
//销毁
void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;

}
//打印元素
void HeapPrint(HP* php)
{
	assert(php);
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}
//取堆顶元素
HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}
//判空
bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

❤️ 结语

 文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~

更多推荐

Spring复杂对象的3中创建方法

复杂对象是相对于简单对象可以直接new出的对象。这种对象在Spring中不可以通过简单对象的创建方式来创建。下面我们将通过实现FactoryBean接口、实例工厂、静态工厂三种方法来创建。FactoryBean接口Spring提供FactoryBean接口并且提供了getObject方法是为了支持自定义工厂。通过实现这

【基础篇】二、parent继承、starter、引导类、内嵌tomcat

文章目录0、前言1、继承parent2、starter3、引导类4、内嵌tomcat5、SpringBoot项目快速启动0、前言SpringBoot,Boot,鞋子,其设计目的是用来简化Spring框架应用的初始搭建以及开发过程Spring程序缺点:依赖设置繁琐(各个依赖之间版本的适配、依赖排除等活儿得自己调)配置繁琐

Hive的分区和分桶

目录​编辑一、Hive分区1.1分区产生的背景1.2动态分区1.2.1hive的动态分区介绍1.2.2动态分区配置1.2.2.1动态分区开启1.2.2.2动态分区模式1.2.2.3一个mr节点上,设置动态分区的最大数量1.2.2.4所有mr节点上,设置所有动态分区的最大数量1.2.2.5设置所有mrjob允许创建的文件

软件测试未来的发展趋势以及软件测试进阶路线

【软件测试行业现状】2023年了你还敢学软件测试?未来已寄..测试人该何去何从?【自动化测试、测试开发、性能测试】全球各地的企业每天都在发展变化着,以应对市场挑战,满足日益成熟的客户需求。即使是正在进行的技术进步也会使软件测试专家在实践的过程中更加专注和精确。2021年给软件测试领域带来了新的技术解决方案,以及质量保证

【2023】Git版本控制-远程仓库详解

目录创建远程仓库向远程仓库推送数据文件从第二台主机本地拉取远程仓库数据第一台主机同步远程仓库数据tag标签git忽略文件Git远程仓库是Git版本控制系统的一个概念,它是一个存储Git代码的远程服务器。你可以将本地Git仓库上传到远程仓库,以便与其他人协作开发或备份代码。创建远程仓库远程仓库可以使用第三方平台,如(gi

AI无法提振台积电股价

来源:猛兽财经作者:猛兽财经总结:(1)台积电的股价已经从最高点下跌了18%,很多期权交易员正在押注台积电的股价会进一步下跌。(2)华尔街分析师目前也下调了台积电的收入和盈利预期。台积电(TSM)的股价最近这段时间已经跌回到了了5月份之前的水平,很多期权交易员也正在押注台积电股价将在10月中旬进一步下跌。台积电股价的溢

服务器上一个域名对应多个前端项目的nginx转发配置

场景:当有两个前端项目A,B的时候,项目A(对应端口8000)和项目B(对应端口8001)分别部署在服务器的不同位置,通过服务器ip+端口都能正常访问单独的项目A和项目B;现在要求两个项目共用一个域名~~也就是说访问http://10.111.182.xxx:8000的时候默认访问项目A的资源,访问http://10.

MyBatis 基本使用

文章目录创建项目POJO对象添加配置文件编程式的使用代理方式的使用接口声明映射文件getMapper总结创建项目创建一个普通的Maven项目,然后添加对应的Mybatis和MySQL的相关依赖<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</a

Java中Hashset存储原理底层深挖

上课老师讲了Hashset的添加元素方法,感觉不甚准确,于是下课扒一扒底层源码,这一看,霍!原来如此。现在小丁来捋一遍他的存储原理。publicbooleanadd(Ee){returnmap.put(e,PRESENT)==null;}可以看到PRESENT是一个privatestaticfinal修饰的object

深入浏览器的渲染原理

一、网页的解析过程大家有没有深入思考过:一个网页URL从输入到浏览器中,到显示经历过怎么样的解析过程呢?要想深入理解下载的过程,我们还要先理解,一个index.html被下载下来后是如何被解析和显示在浏览器上的二、浏览器渲染流程1.浏览器的内核常见的浏览器内核有Trident(三叉戟):IE、360安全浏览器、搜狗高速

【java】【SpringBoot】【三】开发实用篇 基于SpringBoot整合任意第三方技术

目录一、热部署1、手动启动热部署2、自动启动热部署3、热部署范围配置4、关闭热部署二、配置高级1、@ConfigurationProperties2、宽松绑定/松散绑定3、常用计量单位绑定4、数据校验三、测试1、加载测试专用属性2、加载测试专用配置3、web环境模拟测试3.1模拟端口3.2虚拟请求测试3.3匹配响应执行

热文推荐