数据结构——二叉树(2)

news2024/11/29 12:52:12

接上一篇文章http://t.csdnimg.cn/nsKsW,本次我们接着讲解关于二叉树的相关知识。

一、二叉树的相关性质:

1. 若规定根节点的层数为 1 ,则一棵非空二叉树的 i 层上最多有 2^(i-1) 个结点.
2. 若规定根节点的层数为 1 ,则 深度为 h 的二叉树的最大结点数是 2^(h-1)
3. 对任何一棵二叉树 , 如果度为 0 其叶结点个数为n0  , 度为 2 的分支结点个数为n1  , 则有n0=n1+1
4. 若规定根节点的层数为 1 ,具有 n 个结点的满二叉树的深度h=
5. 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为i 的结点有:
①. i>0 i 位置节点的双亲序号为: (i-1)/2 i=0; i 为根节点编号,无双亲节点
②. 2i+1<n ,左孩子序号: 2i+1; 2i+1>=n 否则无左孩子
③. 2i+2<n ,右孩子序号: 2i+2; 2i+2>=n 否则无右孩子
6.通过孩子找双亲:设孩子的编号为i,则其双亲的编号为A=(i-1)/2;根节点没有双亲;

二、二叉树的存储结构:

(一)、顺序储存(数组)

1.顺序结构存储就是使用 数组来存储 ,一般使用数组 只适合表示完全二叉树 ,因为不是完全二叉树会有空间的浪费。而现实中使用中只有 才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树。
根据上述有几点性质:
2. 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为i 的结点有:
①. i>0 i 位置节点的双亲序号为: (i-1)/2 i=0; i 为根节点编号,无双亲节点
②. 2i+1<n ,左孩子序号: 2i+1; 2i+1>=n 否则无左孩子
③. 2i+2<n ,右孩子序号: 2i+2; 2i+2>=n 否则无右孩子
3.通过孩子找双亲:设孩子的编号为i,则其双亲的编号为A=(i-1)/2;根节点没有双亲;
4.满二叉树或者完全二叉树适合用顺序存储,而非完全二叉树适合用链式存储;

(二)、衍生数据结构——堆:

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆 ( 一种二叉树 ) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

1.堆的概念

堆是一种非线性结构,是特殊的完全二叉树,所以适合用数组存储;

2.堆的分类:

小堆(小根堆):树中任意父亲的值都小于等于其孩子;

大堆(大根堆):树中任意父亲的值都大于等于其孩子;

如下图:

(三)、堆的实现(顺序存储)

一般堆我们用顺序存储的方式实现,即用一维数组,所以定义与顺序表差不多,只是实现逻辑不一样,所以基本定义与销毁等操作就大致讲解。

1.堆的定义:
typedef int HPDatatype;
typedef struct Heap
{
	HPDatatype* a;//一维数组
	int size;//现有元素个数
	int capacity;//当前结构最大空间
}HP;
2.堆的初始化:
//初始化
void HPinit(HP* php)
{
	assert(php);
	php->size = 0;
	php->capacity = 0;
	php->a = NULL;
}
3.堆的销毁
//销毁
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}
4.堆的打印:
//打印
void HPprint(HP* php)
{
	assert(php);
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}
5.插入数据:

因为堆是特殊的完全二叉树,所以插入算法与顺序表完全不同;

我们以实现小堆为例

①:首先我们应该考虑是否堆满,根据我们定义所示,当size==capacity时即为堆满,此时我们需要进行扩容方式,因为只有此处可能进行扩容,所以不用单独分装成一个函数,扩容方式与之前的顺序表等等结构相似,所以小编不做多余讲解;

②:根据完全二叉树的顺序存储结构来看,我们知道数组的尾元素即为完全二叉树的尾元素,所以我们插入数据只需在数组的尾部进行插入,又因为堆是特殊的完全二叉树,小堆即双亲结点的值比其所有孩子的值要小,所以当数据插入后,还要将数据与其双亲进行比较,若不满足条件,我们要进行数据的交换,而且我们需要循环进行此操作,直到比较完根节点,又因为我们是不断在找双亲,所以我们称这种方法为“向上调整”,向上调整的前提是前面的结构已经是堆结构了。

③:我们既然要找双亲,所以我们需要牢记双亲结点与孩子结点之前的位置关系,即为上述的几条完全二叉树的性质,向上调整具体算法如下图:

④:时间复杂度为O(log以2为底的n),因为插入元素的时间复杂度为O(1),向上调整的最坏情况为调整至根结点,即完全二叉树的高度,为log以2为底的n;

⑤,源代码 

//交换函数
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[parent],&a[child]);
			//比较完一组后重定位,向上调整
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			//插入结束
			break;
		}
	}
}

//插入元素
void HPPush(HP* php, HPDatatype x)
{
	assert(php);
	//扩容
	if (php->size == php->capacity)
	{
		php->capacity = (php->capacity == 0 ? 4 : php->capacity * 2);
		HPDatatype* tmp = (HPDatatype*)realloc(php->a, sizeof(HPDatatype) * php->capacity);
		//检查扩容
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}
		php->a = tmp;
	}
	

	//插入元素
	php->a[php->size] = x;
	//检查是否需要向上调整
	AdjustUp(php->a, php->size);
	php->size++;
}
6.删除数据:

首先我们考虑一个问题,删除哪个元素有意义呐?

很明显,删除根节点最有意义,因为在大堆中,根节点是最大值;在小堆中,根节点是最小值;所以删除根节点比较有意义一些;

很多小伙伴可能会想,“删除根结点无非就是将数组元素挪动直接覆盖嘛”,答案是不行的,因为我们要清楚一点,堆结构只是孩子与双亲有关系,但孩子之间和兄弟之间是没有关系的,所以挪动数据覆盖元素可能会导致孩子或者兄弟错位,从而覆盖后可能就不是堆结构了;

下面介绍一种思路:上面插入数据用到“向上调整”,现在我们删除数据就用到“向下调整”;

向下调整思路(以小堆结构为例)

①:先交换根结点和尾结点的值;

②:删除尾结点(数组总元素size减1)

③:再找出根结点的两个孩子中较小的孩子,然后交换双亲与较小孩子的值;

④:接着对双亲和孩子重定位,依次向下调整;

注意:其中很多细节应当尤其注意,如可能有些情况没有右孩子等等,具体思路看注释;

//向下调整
void AdjustDown(HPDatatype* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child<n)
	{
		//找出小孩子,同时要注意有没有右孩子,防止child+1越界
		if (a[child] > a[child + 1]&&child+1<n)
		{
			child++;
		}
		//交换
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//继续向下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//删除元素
void HPPop(HP* php)
{
	assert(php);
	//判断堆空
	assert(php->size > 0);
	//交换首尾结点
	Swap(&php->a[0], &php->a[php->size - 1]);
	//删除尾结点,因为是数组,所以直接将现有元素size-1不访问即可
	--php->size;
	//向下调整
	AdjustDown(php->a, php->size, 0);
}

7.取堆顶元素(取根节点)

//取堆顶(取根结点)
HPDatatype HPTop(HP* php)
{
	assert(php);
	//判断是否为堆空
	assert(php->size > 0);
	return php->a[0];
}

8.判空

//判空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

本文章未完待续

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

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

相关文章

10.Form表单中Input输入框设置autoComplete=“off“ 不生效

一、问题的描述 form表单的 password框 有时候我们并不需要chrome自动填充记住的密码这个效果&#xff0c;如下图 二 、正常的预期是什么&#xff1f; 输入框获取焦点时&#xff0c;不展示chrome的默认行为。 三、问题产生的原因分析 发现antd的Input组件的 autocomplete“o…

学习伦敦银交易经验的好方法:亏损

要掌握伦敦银交易的技巧&#xff0c;除了看书学习以外&#xff0c;实践的经验也是很重要的&#xff0c;而这些实践的经验中&#xff0c;从亏损中学习会让经验会更加立体和深刻。下面我们就来讨论一下亏损这个学习伦敦银交易技巧的方法。 首先我们需要了解&#xff0c;不论是伦敦…

语音芯片故障的原因简述

语音芯片在语音设备或者相关产品中应用时会出现故障情况&#xff0c;常见的故障情况更多的是无法发出声音或者声音不连贯&#xff0c;还有声音播报不完整或者混乱等情况。下面让我们来探究芯片本身内部的故障问题&#xff0c;以及外部的原因。 芯片内部自身的故障&#xff1a;…

MySQL binlog 日志解析后的exec_time导致表示什么时间?

1. exec_time 到底表示什么时间&#xff1f; MySQL binlog日志解析后&#xff0c;我们能看到会有 exec_time &#xff0c;从字面意思理解这个记录的是执行时间&#xff0c;那这个记录的到底是单条sql的执行时间&#xff1f;还是事务的执行时间&#xff1f;下面通过测试来解读一…

docker 1.13存储路径修改

由于老版本docker还没有data-root配置&#xff0c;特记录一下老版本修改配置。 新版本配置修改参考&#xff1a;https://blog.csdn.net/tootsy_you/article/details/126933702 修改步骤 编辑docker.service服务文件 vim /usr/lib/systemd/system/docker.service在EXStart添加…

图像二值化阈值调整——Triangle算法,Maxentropy方法

一. Triangle方法 算法描述&#xff1a;三角法求分割阈值最早见于Zack的论文《Automatic measurement of sister chromatid exchange frequency》主要是用于染色体的研究&#xff0c;该方法是使用直方图数据&#xff0c;基于纯几何方法来寻找最佳阈值&#xff0c;它的成立条件…

基于ssm的线上旅行信息管理系统(有报告)。Javaee项目,ssm项目。

演示视频&#xff1a; 基于ssm的线上旅行信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0…

为什么开源mes更有生命力?

所谓开源&#xff0c;就是软件的源码开放&#xff0c;大家都能下载到源代码&#xff0c;一起研究源代码并对软件进行优化和改进。越来越多的IT公司对开源持开放态度&#xff0c;一方面有了优秀的开源项目&#xff0c;就不用再重复造轮子&#xff0c;可以直接使用&#xff1b;另…

天池AI练习生计划 - 第一期Pyhton入门与实践 正式上线!通关赢取双重礼品!

天池AI练习生养成计划是为天池入门学习用户准备的训练营&#xff0c;用户通关后可获得学习奖励&#xff0c;从学习者蜕变为AI新星&#xff01; 轻松来闯关&#xff0c;即可领取双重礼品~ 实训培训证书&#xff1a;通关两个关卡即可领取 阿里云定制鼠标&#xff1a;通关全部关…

强化学习:原理与Python实战

文章目录 1. 引言2. 时间旅行和平行宇宙3. 强化学习4. 策略梯度算法5. 代码案例6. 推荐阅读与粉丝福利 1. 引言 时间循环是一类热门的影视题材&#xff0c;其设定常常如下&#xff1a;主人公可以主动或被动的回到过去。与此同时&#xff0c;主人公会希望利用这样的机会改变在之…

前端js实现将数组对象组装成自己需要的属性,或者去掉对象中不必要的属性

前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 需求&#xff1a;前端js实现将数组对象组装成自己需要的属性&#xff0c;或者前端js实现去掉对象中不必要的属性 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、示例数组对象…

mongodb通过mongoexport命令导出数据

一、mongoexport命令参数 我们通过mongoexport --help来查看这个命令支持的参数 二、mongoexport几个常用参数的演示 2.1、导出所有数据&#xff0c;格式为json格式 –type 用来指定导出的数据格式&#xff0c;可以导出为.json或者.csv mongoexport --host localhost --…

踩准AI时代风口,NFPrompt让人人都能成为赚取利润的创作者

★ AI寒武纪时代&#xff0c;抓住风口并不难 众所周知&#xff0c;随着ChatGPT的面世&#xff0c;AI在2023年快速爆发&#xff0c;不少人已经意识到AI将在未来能够影响到我们每个人生活方方面面&#xff0c;同时AI也将打破现有的经济与社会格局。对于普通人来说&#xff0c;如…

【Servlet】 三

本文主要介绍了基于serlvet的表白墙项目的编写. (附完整代码) 一.JS基础 作为后端开发,对于前端的要求是能前端代码能看懂七七八八 . JS是一个动态弱类型的编程语言 1. let/war定义变量 (推荐使用let) 2.querySelector是浏览器提供api , 能够获取到页面的元素的 (js的目的就…

aardio 去除收尾空格字符

废话不多说 直接开干&#xff01; 知识点 import console type 关键函数,用于获取对象的数据类型 eval 运行aardio代码&#xff0c;并计算表达式的值 assert 断言函数 assertf 反断言函数 error 抛出异常 tostring 用于转换参数为字符串 topointer 用于转换参数为指针 tonumber…

Nature Food | 南农蒋建东组揭示植物有益细菌的全球分布格局及未来变化(完整版)...

到2100年&#xff0c;化石燃料依赖性情景可能导致全球土壤中植物有益细菌丰度大幅下降 Fossil-fuel-dependent scenarios could lead to a significant decline of global plant-beneficial bacteria abundance in soils by 2100 Article&#xff0c;2023-10-30&#xff0c;Nat…

数据库存储过程

存储过程&#xff1a; 是一组为了完成特定功能的sql语句的集合。类似于函数。 写好了一个存储过程之后&#xff0c;我们可以像函数一样随时调用sql的集合。 复杂的&#xff0c;需要很多sql语句联合执行完成的任务。 存储过程在执行上比sql语句的执行速度要快&#xff0c;效率…

09 # 手写 some 方法

some 使用 some() 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。如果在数组中找到一个元素使得提供的函数返回 true&#xff0c;则返回 true&#xff1b;否则返回 false。它不会修改数组。 ele&#xff1a;表示数组中的每一个元素index&#xff1a;表示数…

IDA Pro正版多少钱?本文告诉你!

在软件反向工程和安全分析领域&#xff0c;IDA Pro无疑是一款标志性的工具。这款软件在全球范围内有广泛的应用&#xff0c;用于分析、审计和调试各类软件产品。但是&#xff0c;当我们决定购买这款业界顶级的反汇编和反编译软件时&#xff0c;一个最直接也是最现实的问题摆在面…

Oracle Unifier 22.12 ~ 23.10 功能改进清单表

序言 时隔近一年&#xff0c;Oracle Unifier 22还没握熟&#xff0c;新版本23便已迭代到23.10&#xff0c;根据甲骨文常规的发布规律&#xff0c;相信不久之后便会正式迎来正式本地版V23&#xff0c;了解Unfier的朋友或许知晓&#xff0c;本地版是云版迭代一年后的版本&#x…