【数据结构初阶】第七篇——二叉树的顺序结构及实现(堆的向下,向上调整算法)

news2024/11/20 0:27:10

二叉树的顺序结构

堆的概念及结构

堆的向下调整算法

堆的向上调整算法

堆的实现

初始化堆

销毁堆

打印堆

堆的插入

堆的删除

获取堆顶的数据

获取堆的数据个数

堆的判空

建堆的时间复杂度


二叉树的顺序结构

普通二叉树是不适合用数组来存储的,因为可能会导致大量的空间浪费。而完全二叉树更适合使用顺序结构存储。

堆的概念及结构

堆的概念

堆:如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足ki<=k2i+1且ki<=k2i+2(或满足ki>=k2i+1且ki>=k2i+2),其中i=0,1,2...,则称该集合为堆。

小堆:将根节点最小的堆叫做小堆,也叫作最小堆或小根堆。

大堆:将根节点最大的堆叫做大堆,也叫最大堆或大跟堆。

堆的性质:

堆中某个节点的值总是不大于或不小于其父节点的值。

堆总是一颗完全二叉树。

堆的结构

堆的向下调整算法

现在我们给出一个数组,逻辑上可以看作是一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。

但是,使用向下调整算法需要满足一个前提:

    若将其调整成小堆,那么根节点的左右子树必须都为小堆。

    若将其调整成大堆,那么根结点的左右子树必须都为大堆。 

向下调整算法的基本思想(这里以小堆为例):

1.从根节点处开始,选出左右孩子中值较小的孩子。

2.让小的孩子与其父亲进行比较。

若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,知道调整到叶子节点为止。

若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

//交换函数
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

//堆的向下调整(小堆)
void AdjustDown(int* a, int n, int parent)
{
	//child记录左右孩子中值较小的孩子的下标
	int child = 2 * parent + 1;//先默认其左孩子的值较小
	while (child < n)
	{
		if (child + 1 < n&&a[child + 1] < a[child])//右孩子存在并且右孩子比左孩子还小
		{
			child++;//较小的孩子改为右孩子
		}
		if (a[child] < a[parent])//左右孩子中较小孩子的值比父结点还小
		{
			//将父结点与较小的子结点交换
			Swap(&a[child], &a[parent]);
			//继续向下进行调整
			parent = child;
			child = 2 * parent + 1;
		}
		else//已成堆
		{
			break;
		}
	}
}

   使用堆的向下调整算法,最坏的情况下就是一直交换,需要循环的次数为:h-1(h为树的高度),而 h = log2(N+1)(N为树的总结点数).所以堆的向下调整算法的时间复杂度为:O(logN) 。

   上面说到,使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?
 答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。

代码:

	//建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->a, php->size, i);
	}

堆的向上调整算法

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。

向上调整算法的基本思想(以建小堆为例):

1.将目标结点与其父结点比较。

2.若目标结点的值比其父节点的值小,则交换目标节点与其父节点的位置,并将原目标节点的父节点当作新的目标节点继续进行向上调整。若目标节点的值比其父节点的值大,则停止向上调整,此时该树已经是小堆了.

代码如下:

//交换函数
void Swap(HPDataType* x, HPDataType* y)
{
	HPDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

//堆的向上调整(小堆)
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)//调整到根结点的位置截止
	{
		if (a[child] < a[parent])//孩子结点的值小于父结点的值
		{
			//将父结点与孩子结点交换
			Swap(&a[child], &a[parent]);
			//继续向上进行调整
			child = parent;
			parent = (child - 1) / 2;
		}
		else//已成堆
		{
			break;
		}
	}
}

堆的实现

初始化堆

   首先,必须创建一个堆类型,该类型中需包含堆的基本信息:存储数据的数组,堆中元素的个数以及当前堆的最大容量。

typedef int HPDataType;//堆中存储数据的类型

typedef struct Heap
{
	HPDataType* a;//用于存储数据的数组
	int size;//记录堆中已有元素个数
	int capacity;//记录堆的容量
}HP;

 堆的初始化和顺序表很相似基本上什么都不用做,只要指针置空,大小和容量置为0即可。

void HeapInit(HP* hp)
{
  assert(hp);
  hp->a=NULL;
  hp->capacity=hp->size=0;

销毁堆

为了避免内存泄漏,使用完动态开辟的内存空间后都要及时释放该空间,所以,一个用于释放内存空间的函数是必不可少的。

//销毁堆
void HeapDestroy(HP* php)
{
	assert(php);

	free(php->a);//释放动态开辟的数组
	php->a = NULL;//及时置空
	php->size = 0;//元素个数置0
	php->capacity = 0;//容量置0
}

打印堆

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

堆的插入

数据插入时是插入到数组的末尾,即树形结构的最后一层的最后一个结点,所以插入数据后我们需要运用堆的向上调整算法对堆进行调整,使其在插入数据后仍然保持堆的结构。

//堆的插入
void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, 2 * php->capacity*sizeof(HPDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;
	//向上调整
	AdjustUp(php->a, php->size - 1);
}

堆的删除

堆的删除,删除的是堆顶的元素,但是这个删除过程可并不是直接删除堆顶的数据,而是先将堆顶的数据与最后一个结点的位置交换,然后再删除最后一个节点,再对堆进行一次向下调整。

原因:我们若是直接删除堆顶的数据,那么原堆后面数据的父子关系就全部打乱了,需要全体重新建堆,时间复杂度为O ( N ) O(N)O(N)。若是用上述方法,那么只需要对堆进行一次向下调整即可,因为此时根结点的左右子树都是小堆,我们只需要在根结点处进行一次向下调整即可,时间复杂度为O ( log ⁡ ( N ) ) O(\log(N))O(log(N))

//堆的删除
void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	Swap(&php->a[0], &php->a[php->size - 1]);//交换堆顶和最后一个结点的位置
	php->size--;//删除最后一个结点(也就是删除原来堆顶的元素)
	AdjustDown(php->a, php->size, 0);//向下调整
}

获取堆顶的数据

获取堆顶的数据,即返回数组下标为0的数据

//获取堆顶的数据
HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	return php->a[0];//返回堆顶数据
}

获取堆的数据个数

获取堆的数据个数,即返回堆结构体中的size变量

//获取堆中数据个数
int HeapSize(HP* php)
{
	assert(php);

	return php->size;//返回堆中数据个数
}

堆的判空

堆的判空,即判断堆结构体中的size变量是否为0

//堆的判空
bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;//判断堆中数据是否为0
}

建堆的时间复杂度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为nginx配置好看的错误提示页面

前言 nginx默认错误页面确实有些丑哈&#xff0c;leeader让我换一个样式 &#xff0c;我就来喽&#xff01; 为nginx配置好看的错误提示页面前言1 找异常页原始页2 win上替换3 再linux服务器上替换4 不生效解决办法样式显示不正确6 错误页源码1 找异常页 原始页 nginx默认错误…

2个月快速通过PMP证书的经验

01 PMP证书是什么&#xff1f; 指的是项目管理专业人士资格认证。它是由美国项目管理协会&#xff08;Project Management Institute(简称PMI)&#xff09;发起的&#xff0c;严格评估项目管理人员知识技能是否具有高品质的资格认证考试。其目的是为了给项目管理人员提供统一的…

初学者的Metasploit教程 - 从基础到高级

Metasploit是使用最广泛的渗透测试工具之一&#xff0c;是一个非常强大的多合一工具&#xff0c;用于执行渗透测试的不同步骤。 文章目录前言安装Metasploit在 Linux 上安装 Metasploit了解 Metasploit 的版本并更新渗透测试的基础知识1. 信息收集/侦察2. 漏洞分析3.渗透4. 渗透…

OSCP_VULHUB_Hack the Kioptrix Level-1.2

文章目录前言渗透方法论&#xff08;方法一&#xff09;渗透方法论&#xff08;方法二&#xff09;第一种sqlmap扫描&提取数据库和用户凭证ssh登录使用 SUID 位和 SUDO 二进制文件利用目标第二种方法searchsploit LotusCMS前言 Kioptrix 的 CTF 挑战&#xff1a;Level1.2 …

Linux搭建Hyperledger Fabric区块链框架 - Hyperledger Fabric 概念

企业选型的区块链底层技术 Hyperledger Fabric 概念 2015年&#xff0c;Linux基金会启动了Hyperledger项目&#xff0c;目标是发展跨行业的区块链技术。 Hyperledger Fabric是Hyperledger中的一个区块链项目&#xff0c;包含一个账本&#xff0c;使用智能合约并且是一个通过所…

上海亚商投顾:三大指数均涨约1% 两市近4300股飘红

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪三大指数早盘冲高回落&#xff0c;午后又震荡走强&#xff0c;深成指、创业板指均涨超1.2%。人工智能概念掀涨停潮&a…

Pytorch实战笔记(3)——BERT实现情感分析

本文展示的是使用 Pytorch 构建一个 BERT 来实现情感分析。本文的架构是第一章详细介绍 BERT&#xff0c;其中包括 Self-attention&#xff0c;Transformer 的 Encoder&#xff0c;BERT 的输入与输出&#xff0c;以及 BERT 的预训练和微调方式&#xff1b;第二章是核心代码部分…

机器视觉_HALCON_HDevelop用户指南_4.HDevelop开发程序

文章目录四、HDevelop编程4.1. 新建一个新程序4.2. 输入一个算子4.3. 指定参数4.4. 获取帮助4.5. 添加其他程序4.6. 理解图像显示4.7. 检查变量4.8. 利用灰度直方图改进阈值4.9. 编辑代码行4.10. 重新执行程序4.11. 保存程序4.12. 选择特征区域4.13. 打开图形窗口4.14. 循环遍历…

Swig工具在win10上使用

SWIG 是一种软件开发工具&#xff0c;它将 C 和 C 编写的程序与各种高级编程语言连接起来。这里我们用它来将 C/C 转换成 Java。 一、Swig安装 1、下载 官网&#xff1a;SWIG官网下载 源码链接 GitHub&#xff1a;https://github.com/swig/swig.git 这两个地址可能会出现无…

STM32单片机智能蓝牙APP加油站火灾预警安防防控报警监控系统MQ2DHT11

实践制作DIY- GC0122-智能蓝牙APP加油站火灾预警 一、功能说明&#xff1a; 基于STM32单片机设计-智能蓝牙APP加油站火灾预警 功能介绍&#xff1a; 基于STM32F103C系列最小系统&#xff0c;MQ-2烟雾传感器&#xff0c;火焰传感器&#xff08;不能直视阳光会受到阳光干扰&…

Cesium 渐变长方体实现-Shader

position获取: 1.1 在cesium中,可通过vec4 p = czm_computePosition();获取 模型坐标中相对于眼睛的位置矩阵 1.2 vec4 eyePosition = czm_modelViewRelativeToEye * p; // position in eye coordinates 获取eyePosition 1.3 v_positionEC = czm_inverseModelView * eyePo…

Python流程控制详解

和其它编程语言一样&#xff0c;Python流程控制可分为 3 大结构&#xff1a;顺序结构、选择&#xff08;分支&#xff09;结构和循环结构。 Python对缩进的要求&#xff08;重点&#xff09; Python 是一门非常独特的编程语言&#xff0c;它通过缩进来识别代码块&#xff0c;…

ConditionalOnBean详解及ConditionalOn××总结

ConditionalOnBean详解 为什么学习ConditionalOnBean 在学习 Springboot 自动装配的时候遇到 Bean 装配和 Bean 配置需要条件判断的场景时&#xff0c;查阅了相关内容了解到 Conditional 和 ConditionalOnBean 注解&#xff0c;深入学习之后受益匪浅。 ConditionalOnBean测试…

后量子 KEM 方案:Newhope

参考文献&#xff1a; Lyubashevsky V, Peikert C, Regev O. On ideal lattices and learning with errors over rings[J]. Journal of the ACM (JACM), 2013, 60(6): 1-35.Lyubashevsky V, Peikert C, Regev O. A toolkit for ring-LWE cryptography[C]//Advances in Cryptol…

Linux常见指令大全(一)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

POE交换机全方位解读(上)

POE交换机在安防行业的应用&#xff0c;给视频监控系统带来了质的改变&#xff0c;POE交换机。可通过网线为无线AP、网路摄像头等PoE终端设备供电&#xff0c;传送距离可达100m&#xff0c;安装简单&#xff0c;即插即用。非常适合无线城市、安防监控等行业使用。 POE供电方案及…

「融云政企数智办公解决方案」入选「大信创产品目录」

1月31日&#xff0c;CIO 时代、新基建创新研究院联合公布“大信创产品目录”&#xff0c;“融云政企数智办公解决方案”成功通过审核&#xff0c;被正式纳入“大信创产品目录”。 据悉&#xff0c;CIO 时代、新基建创新研究院从去年底开始组织开展“大信创产品目录”征集工作&a…

【C语言 数据结构】数组与对称矩阵的压缩存储

文章目录数组的定义数组的顺序表示和实现顺序表中查找和修改数组元素矩阵的压缩存储特殊矩阵稀疏矩阵数组的定义 提到数组&#xff0c;大家首先会想到的是&#xff1a;很多编程语言中都提供有数组这种数据类型&#xff0c;比如 C/C、Java、Go、C# 等。但本节我要讲解的不是作为…

frp构建多级网络代理

简介frp 是一个专注于内网穿透的高性能的反向代理应用&#xff0c;支持 TCP、UDP、HTTP、HTTPS 等多种协议&#xff0c;采用 Golang 编写&#xff0c;支持跨平台&#xff0c;仅需下载对应平台的二进制文件即可执行&#xff0c;没有额外依赖。frp可以将内网服务以安全、便捷的方…

Idea 中【Maven】的环境配置

目录 一 maven 项目管理工具软件二.首先要安装Jdk1.7/8 和IDEA三.在IDEA中配置maven四.在MavenDemo01下 创建多个模块项目四.Jar包依赖 插件五.运用一 maven 项目管理工具软件 1 . Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具…