二叉树的介绍及其顺序结构的实现

news2024/9/17 8:57:01

Hello, 亲爱的小伙伴们,你们的作者菌又回来了,之前我们学习了链表、顺序表、栈等常见的数据结构,今天我们将紧跟之前的脚步,继续学习二叉树。

好,咱们废话不多说,开始我们今天的正题。

1.树

1.1树的概念和结构

树是一种非线性的数据结构,他是由n(n >0)个有限的节点组成的一个具有层次结构的集合。

树是因为他的外形看起来像是一颗倒挂的树,也就是根朝上,叶子朝下。

有一个特殊的节点———根节点,根节点没有前驱节点。

除根节点外,其余的节点都被分成了M(M>0)个互不相交的集合,其中每一个集又是和树结构相似的子树,每颗子树的根节点有且只有一颗根节点,可以有无数个子节点,因此树是由递归定义的!

在树型结构中,子树不能有交集,否则就不是树的结构。

比如以下的非树形结构:

 子树是不相交的。

除了根节点外,每个节点有且只有一个父节点。

一颗有N个节点的树有N-1个边。

1.2树的相关术语

 父节点/双亲结点:若有一个节点含有子节点,则这个节点成为子节点的父节点;如上图中的B的父节点为A.

子节点/孩子节点:一个节点含有的子树的根节点,如上图中·:B是A的孩子节点。

节点的度:一个节点有几个孩子节点,他的度就是多少,比如:A的度是2;F的度是2

树的度:树的度指的就是最大节点的度。

分支节点:度不为0的节点。

兄弟节点:具有相同父节点的节点成为兄弟节点,比如E、F节点就是兄弟节点。

节点的层次:从跟开始定义起,跟为第1层,根节点的子节点为第2层,以此类推。

树的高度和深度:树中节点的最大层次。比如上面的树的最大层次就是4,所以这棵树的深度就是4.

子孙:以某节点为根的子树中任意节点都可以称为该节点的子孙。

森林:多颗不相干的树的集合就是森林。

1.3树的表示

孩子兄弟表示法:

树的结构相较于线性表就要复杂的多,要存储起来也比较麻烦,既要保存值又要保存节点与节点之间的关系,实际上树的表示方式有很多种,比如:双亲表示法、孩子表示法、孩子双亲表示法、以及孩子兄弟表示法等,这里我们就简单的看看孩子兄弟表示法:

struct TreeNode
{
struct Node* child; // 左边开始的第⼀个孩⼦结点
struct Node* brother; // 指向其右边的下⼀个兄弟结点
int data; // 结点中的数据域
}

1.4树形结构的应用场景 

文件系统是计算机存储和文件管理的一种方式,他利用树形结构来组织和管理文件和文件夹,在文文件夹中,树的结构被广泛的应用,他通过父节点和子节点之间的关系来展示不同层级的文件和文件夹之间的关系。

 2.二叉树

2.1概念与结构

在树的结构中。我们常见的就是二叉树,一颗二叉树的节点的设计一个有限的集合,该集合是由根节点加上两颗别称为左子树和右子树的集合所构成的。

从上面的图我们能得出一些相关的信息:
1.二叉树不存在度大于2的节点。

2.二叉树的子树又左右之分,次序不能颠倒,因此二叉树是有序树。

注意:对于任何的二叉树都是由以下的情况复合而成的:

 

 2.2特殊的二叉树

一颗二叉树,如果每一层的节点数都达到了最大值,则这棵树就是满二叉树。也就是说,如果这颗树的层数是k。且节点的总数是2^k - 1,则他就是满二叉树。

完全二叉树是效率非常高的数据结构,完全二叉树是由满二叉树的概念引述而来的。对于深度为k的,有n个节点的二叉树吗,当且仅当其每一个节点都与深度为k的满二叉树中的编号从1到n的节点一一对应就可称其为二叉树,要注意:满二叉树是一种特殊的完全二叉树.

2.3二叉树的存储结构

二叉树一般可以使用两种储存结构,一种是顺序结构,一种是链式结构 

2.3.1顺序结构

顺序结构存储就是使用数组作为底层结构,一般数组适用于表示完全二叉树,因为不是完全二叉树就有空间的浪费,完全二叉树更适合使用顺序结构存储。

 3.实现顺序结构二叉树

一般对堆使用使用顺序结构的数组来存储数组,堆是一种特殊的二叉树,具有二叉树的特性同时,还具备其他的特性。

3.1堆的结构和概念

如果有一个关键码的集合K = {k_{0},k_{1},k_{2},k_{3},.....k_{n -1}},他把所有的元素按照完全二叉树的存储结构存储起来,在一个一维数组中,并满足^{_{}}k_{i} <= k_{2*i + 1}k_{i}>= k_{2*i + 1}k_{i}<= k_{2*i+2})i = 0、1、2、3.....,则称之为小堆(或大堆)。将根节点最大的堆叫做最大堆或者是大根堆,根节点最小的堆叫做小堆和小根堆。

堆具有以下的性质:

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

堆是一颗完全二叉树 

• 对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从
0 开始编号,则对于序号为 i 的结点有:
1. 若 i>0 , i 位置结点的双亲序号: (i-1)/2 ; i=0 , i 为根结点编号,⽆双亲结点
2. 若 2i+1<n ,左孩⼦序号: 2i+1 , 2i+1>=n 否则⽆左孩⼦

3. 若 2i+2<n ,右孩⼦序号: 2i+2 , 2i+2>=n 否则⽆右孩⼦

 3.2堆的实现

首先我们还是要先创建三个文件,来实现堆:

3.2.1堆的定义

底层的结构·是数组,所以对结构的定义就和顺序表的定义差不多

typedef int HpDataType;
typedef struct Heap
{
	HpDataType *arr;
	int size;//记录有效元素的个数
	int capacity;//记录申请的空间容量
}Hp;

3.2.2堆的初始化(HpInit)和销毁(HpDestroy)

由于底层结构就是由数组构成的,所以在初始化上,顺序表和堆十分的相似:

1.HpInit

定义:

//堆的初始化
void HpInit(Hp* php);

实现:

/堆的初始化
void HpInit(Hp* php)
{
	assert(php);
	php->capacity = php->size = 0;
	php->arr = NULL;
}

2.HpDstroy

定义:

//堆的销毁
void HpDstroy(Hp* php);

实现:

void HpDstroy(Hp* php)
{
	assert(php);
	free(php->arr);
	php->arr = NULL;
	php->capacity = php->size = 0;
}

由于底层结构的相似性,我们在实现堆的从初始化和销毁时,就和顺序表的初始化和销毁相似。

测试:

初始化时,s.arr = NULL; s.capacity = s.size = 0;

3.2.3 堆数据的插入(HpPush)(入堆)

堆数据的插入和顺序表的数据插入也有相似之处:

定义:

//堆的数据插入
void HpPush(Hp* php, HpDataType x);

实现:


void HpPush(Hp* php, HpDataType x)
{
	assert(php);
	//判断空间是否足够
	if (php->capacity == php->size)
	{
		//扩容
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HpDataType* ptmp = (HpDataType*)realloc(php->arr, sizeof(HpDataType) * newcapacity);
		if (ptmp == NULL)
		{
			perror("realloc Fail!!");
			exit(1);
		}
		php->arr = ptmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size++] = x;
	AdjustUp(php->arr, php-> size - 1);//向上调整算法

}

和顺序表的数据插入相似,我们都要先确保空间的充足,才能进行插入操作!!

重要的是,我们需要理解向上调整算法的含义:

3.2.3.1向上调整算法(AdjustUp)

向上调整算法:将新的数据插入到数组的尾部,在进行向上的调整,直到满足堆的结构!!

先将元素插到堆的末尾,即最后一个孩子之后

插入之后如果堆的性质遭到破坏,将新插入的节点顺着其双亲往上调整到合适得位置就好额!!

 代码实现:

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void AdjustUp(HpDataType* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)//当child节点为0是,该节点已经到达这颗树的顶端,无需再调整!!
	{
		if (arr[child] > arr[parent])//这里采用 < 最后就能建成小堆;采用>就能建成大堆
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1 )/ 2;
		}
		else
		{
			break;
		}
	}
}

代码测试:

我们建成的是大堆吗?

我们画图来验证一下:

 

 从这里我们就能看出,建成大堆成功!!

3.2.4堆中数据的删除(HpPop)(出堆)

注意:出堆,与出队列的定义相似,出堆是将堆头的数据删去!!

定义:

//堆中数据的删除!!
void HpPop(Hp* php);

实现:
我们可以怎样来删去堆头的数据呢?

也许,我们可以让堆头的数据与堆尾的数据相交换,删除堆尾的数据容易,且不会改变原来的数据结构。

所以我们又会涉及到一种在堆中的向下调整算法:

3.2.4.1向下调整算法(AdjustDown)

注意:向下调整算法有一个前提:左右子树必须为一个堆!! 

1.将堆顶元素与最后一个元素交换 

2.删除堆中的最后一个元素

3.将堆顶的元素向下调整直到满足堆的特性为止!

 

 代码实现:

void AdjustDown(int* arr, int parent, int n)
{
	int child = parent  * 2 + 1;
	if (arr[child] < arr[child + 1])
	{
		child++;
	}
	while (child < n)//注意:child最大时为 n-1,注意不能越界!!
	{
		if (arr[child] > arr[parent])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}

所以完整的代码我们就可以写成:

void AdjustDown(int* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	if (arr[child] < arr[child + 1])//找出左右孩子节点中数值较大那个
	{
		child++;
	}
	while (child < n)
	{
		if (arr[child] > arr[parent])//如果孩子节点的值比父节点大,就进行交换
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}
//堆中数据的删除!!
void HpPop(Hp* php)
{
	assert(php && php->size);
	//最后一个元素与第一个元素相交换
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	php->size--;

	//开始向下调整
	AdjustDown(php->arr, 0, php->size);
}

代码测试:

我们来比对一下删除元素前后的效果:

 

 可以看到交换后依然符合大堆的性质!!!

3.2.5取堆顶元素和堆的判空操作(HpTop && HpIsEmty)

在实现堆的部分性质时,我们很容易就会联想到,我们曾经学习过的数据结构,如:栈、队列和顺序表单链表等。

这里的取堆顶元素就和栈的性质相似,堆的数据也不能进行随机访问,所以,想要进行堆数据的打印,就要进行数据的以此访问和打印,所以我们需要结合判空、取堆顶元素和去堆顶元素来实现堆数据的打印。

判空和取堆顶数据的定义:
 

//取堆顶元素
HpDataType HpTop(Hp* php);
//判空
bool HpIsEmpty(Hp* php);

这里的两个函数实现和之前的操作相似,我们这里就不多叙述,代码也十分的简单,感兴趣的朋友也可以去看看我以前的文章:
 

HpDataType HpTop(Hp* php)
{
	assert(php && php->size);
	return php->arr[0];
}
bool HpIsEmpty(Hp* php)
{
	assert(php);
	return php->size == 0;
}

所以最后我们就可以将堆中的数据打印出来:

这样我们就实现了堆数据的打印了!! 

结语

二叉树的顺序结构我们就学习到这里,感谢大家的阅读,如果你有不懂得问题也欢迎大家来评论区理性讨论,咱们下期再见,拜拜!!

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

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

相关文章

Vue3+.NET6前后端分离式管理后台实战(三十二)

1&#xff0c;Vue3.NET6前后端分离式管理后台实战(三十二)

Java上门做饭平台系统小程序源码

&#x1f37d;️解锁新生活方式&#xff01;揭秘“上门做饭平台系统”的五大魅力&#x1f31f; &#x1f3e0;【懒人福音&#xff0c;美食直达家门】 在这个快节奏的时代&#xff0c;谁不想下班后直接享受热腾腾的家常美味呢&#xff1f;上门做饭平台系统就是你的私人厨师团队…

java基础概念07-switch语句

一、switch定义 二、基本语法 switch (expression) { case value1: // 当expression的值等于value1时执行的代码 break; // 可选 case value2: // 当expression的值等于value2时执行的代码 break; // 可选 // 你可以有任意数量的case语句 default: // 可选 // 当没有…

【Android驱动06】GMS兼容性测试CTS --环境搭建、测试执行、结果分析

CTS即Compatibility Test Suite意为兼容性测试&#xff0c;是Google推出的Android平台兼容性测试机制。其目的是尽早发现不兼容性&#xff0c;并确保软件在整个开发过程中保持兼容性。只有通过CTS认证的设备才能合法的安装并使用Google market等Google应用。 一&#xff0c;搭…

leetcode 二叉树专题——java实现

1. 二叉树中序遍历 给一棵树&#xff0c;输出中序遍历。 树已经给你建好了&#xff0c;按照一下形式&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* Tree…

花10分钟写个漂亮的后端API接口模板!

你好&#xff0c;我是田哥 在这微服务架构盛行的黄金时段&#xff0c;加上越来越多的前后端分离&#xff0c;导致后端API接口规范变得越来越重要了。 比如&#xff1a;统一返回参数形式、统一返回码、统一异常处理、集成swagger等。 目的主要是规范后端项目代码&#xff0c;以及…

数据恢复大师免费版落伍了吗?2024年4大创新恢复工具对比评测

在这个数字时代&#xff0c;要是突然发现电脑里的珍贵照片、视频或者重要文件不见了&#xff0c;那种感觉就像失去了什么重要的东西&#xff0c;让人焦虑又无助。市面上虽然有很多数据恢复软件&#xff0c;但不是所有软件都能满足我们的需求&#xff0c;尤其是那些既免费又好用…

一键解析:由于找不到xinput1_3.dll,无法继续执行代码的问题,有效修复xinput1_3.dll文件

xinput1_3.dll是一个重要的动态链接库文件&#xff0c;它是DirectX软件包的一部分&#xff0c;主要负责处理游戏和多媒体应用程序中的输入功能。当用户尝试启动某些游戏或应用程序时&#xff0c;可能会遇到一个错误提示&#xff0c;指出“由于找不到xinput1_3.dll&#xff0c;无…

开源AI智能名片拓客小程序在企业品牌快速打造中的应用探索

摘要&#xff1a;在数字化浪潮席卷全球的今天&#xff0c;企业品牌的建设与传播已不再局限于传统的广告与营销手段。开源AI智能名片拓客小程序作为一种创新的数字营销工具&#xff0c;凭借其智能化、个性化及高效化的特点&#xff0c;正逐步成为企业品牌快速打造与提升市场影响…

游戏类型有哪些?游戏分类详细解说(一)

这篇文章是来源于游戏工委牵头制定的团体标准&#xff1a;《网络游戏分类》&#xff08;音数协 2023年1月&#xff0c;征求意见稿&#xff09;。 音数协游戏工委是游戏行业自律组织&#xff0c;在其牵头组织下&#xff0c;腾讯、网易、盛趣、索尼、完美世界、巨人网络等单位共…

MTK6983/MT6983天玑9000芯片详细性能参数_MTK联发科5G方案定制

联发科MT6983旗舰5G移动平台处理器集成了蓝牙、FM、WLAN 和 GPS 模块&#xff0c;是一个高度集成的基带平台&#xff0c;集成了调制解调器和应用处理子系统&#xff0c;可支持 LTE/LTE-A/NR 和 C2K 智能手机应用。 该芯片集成了四个 Arm Matterhorn (ELP) 内核、四个 Arm Klein…

Java | Leetcode Java题解之第304题二维区域和检索-矩阵不可变

题目&#xff1a; 题解&#xff1a; class NumMatrix {int[][] sums;public NumMatrix(int[][] matrix) {int m matrix.length;if (m > 0) {int n matrix[0].length;sums new int[m 1][n 1];for (int i 0; i < m; i) {for (int j 0; j < n; j) {sums[i 1][j …

视图库对接系列(GA-T 1400)二十二、视图库对接系列(级联)校时

背景 校时接口一般用的比较少&#xff0c;一般用的话就注册上级成功之后会发送 一个校时&#xff0c;告诉服务端目前客户端的时间情况。 实现 这个实现的话&#xff0c;我们再注册的时候就已经实现了&#xff0c;注册成功就会发送一次校时 先看抓包 具体实现代码 service接…

【转型必看】Java到AI,程序员的逆袭秘籍,转行人工智能不再是梦!

随着技术的不断进步&#xff0c;人工智能&#xff08;AI&#xff09;已经成为当今科技领域最热门的话题之一。许多开发者开始考虑从传统的软件开发领域&#xff0c;如Java&#xff0c;转向人工智能领域&#xff0c;今天小编和大家一起来探讨Java开发者是否可以转型到人工智能&a…

「JavaEE」Spring IoC 1:Bean 的存储

&#x1f387;个人主页 &#x1f387;所属专栏&#xff1a;Spring &#x1f387;欢迎点赞收藏加关注哦&#xff01; IoC 简介 IoC 全称 Inversion of Control&#xff0c;即控制反转 控制反转是指控制权反转&#xff1a;获得依赖对象的过程被反转了 传统开发模式中&…

38 Debian如何配置Keepalived+LVS+NFS实现高可用负载均衡

作者:网络傅老师 特别提示:未经作者允许,不得转载任何内容。违者必究! Debian如何配置Keepalived+LVS+NFS实现高可用负载均衡 《傅老师Debian知识库系列之38》——原创 ==前言== 傅老师Debian知识库特点: 1、拆解Debian实用技能; 2、所有操作在VMware虚拟机实测完成;…

C# Unity 面向对象补全计划 之 初识继承方法与多态

本文仅作学习笔记与交流&#xff0c;不作任何商业用途&#xff0c;作者能力有限&#xff0c;如有不足还请斧正 本系列旨在通过补全学习之后&#xff0c;给出任意类图都能实现并做到逻辑上严丝合缝 1.继承方法 C# & Unity 面向对象补全计划 之 继承&#xff08;字段与属性&…

什么是五力分析?5分钟带你了解它在企业财务经营中的应用与价值!

如今&#xff0c;随着全球化进程的不断加速&#xff0c;市场环境复杂多变&#xff0c;市场竞争日益激烈&#xff0c;财务经营已经成为了企业应对复杂市场环境、保持自身竞争力的关键。体系化的五力分析平台能够为企业提供一套全面的解决方案&#xff0c;帮助企业在盈利能力、偿…

重生奇迹MU 多准备几套装备才能玩得更好

一、时装必须是要准备一套的 我相信每个玩家都非常注重时装&#xff0c;无论是哪个职业&#xff0c;都希望在平常场合下穿上时髦惊艳的服装。在勇者大陆中&#xff0c;这种操作很常见&#xff0c;几乎所有玩家都需要准备一套。 在游戏中&#xff0c;需要具备一套适合的装备才…

Java实现数据库图片上传(包含从数据库拿图片传递前端渲染)-图文详解

目录 1、前言&#xff1a; 2、数据库搭建 &#xff1a; 建表语句&#xff1a; 3、后端实现&#xff0c;将图片存储进数据库&#xff1a; 思想&#xff1a; 找到图片位置&#xff08;如下图操作&#xff09; 图片转为Fileinputstream流的工具类&#xff08;可直接copy&#…