初阶数据结构——二叉树大汇总

news2024/9/17 7:13:30

这篇博客将会讲到二叉树的部分内容及堆的相关知识~ 这里将会涉及到大量的递归(头大)

目录

1.树

1.1树的概念

1.2树的相关概念

1.3树的表示

1.4树的实际应用

2.二叉树

2.1二叉树的概念

2.2特殊的二叉树 

2.2.1 满二叉树

2.2.2 完全二叉树

2.2.3堆 

2.3 二叉树的性质

2.4二叉树的存储 

2.4.1顺序结构存储

2.4.2链式结构存储

2.5二叉树的遍历

2.5.1前序遍历

代码表示 

 2.5.2中序遍历

代码表示 

2.5.3后续遍历

代码表示 

 2.5.4层序遍历 

代码表示

总结示例 

2.6二叉树的其他代码 


1.树

1.1树的概念

树这个东东,真的不同于之前学到的数据结构 

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

  1. 有一个特殊的结点,称为根结点,根结点没有前驱结点
  2. 除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<=i<=m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  3. 树是递归定义的。 

如图: 

 

注意:子树与子树之间不能相连,不然就变成另一种数据结构——图 

 

1.2树的相关概念

 

结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6

叶结点或终端结点度为0的结点称为叶结点; 如上图:B、C、H、I...等结点为叶结点

非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等结点为分支结点

双亲结点或父结点若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

树的度一棵树中,最大的结点的度称为树的度; 如上图:树的度为6

结点的层次从根开始定义起,根为第1层,根的子结点为第2层,以此类推;

树的高度或深度树中结点的最大层次; 如上图:树的高度为4

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林; 

1.3树的表示

树结构相对线性表结构比较复杂,要存储表示起来比较麻烦,因为既要保存,也要保存结点和结点之间的关系

实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法 等。我们这里就简单的了解其中最常用的孩子兄弟表示法

所谓孩子兄弟表示法就是左孩子右兄弟(节点的分支左边第一个是孩子,剩下的都是兄弟)如图所示:

1.4树的实际应用

2.二叉树

2.1二叉树的概念

通俗来讲,二叉树就是度为2的树,也就是节点的度最大为2。如下图:

二叉树可以是空树,任何一棵二叉树都由下面几种情况组成:

2.2特殊的二叉树 

2.2.1 满二叉树

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。也可以叫做正则二叉树

2.2.2 完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

简单来说,就是从第一层到倒数第二层是满二叉树,最后一层从左向右填充(可以不满)

2.2.3堆 

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

简单来说就是:

每一个父亲都比孩子小,叫小堆(头小脚大);每一个父亲都比孩子大,叫大堆(头大脚小)

2.3 二叉树的性质

1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点。

2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2^h -1。

3. 对任何一棵二叉树, 如果度为0的叶结点个数为n,度为2的分支结个数为m ,则有 m=n - 1。 

4. 若规定根结点的层数为1,具有n个结点的满二叉树的深度,h= log(n+1)  (ps: 是log以2 为底,n+1为对数)

5. 对于具有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则无右孩子

2.4二叉树的存储 

二叉树的存储方式有两种,一种顺序结构存储,一种链式结构存储。 

2.4.1顺序结构存储

顺序结构存储也就是用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。 

二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2.4.2链式结构存储

二叉树的链式存储结构是指用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。 

 

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{

	struct BinTreeNode* left;// 指向当前结点左孩子
	struct BinTreeNode* right;// 指向当前结点右孩子
	BTDataType data;// 当前结点值域
}

// 三叉链
struct BinaryTreeNode
{

	struct BinTreeNode* left;// 指向当前结点左孩子
	struct BinTreeNode* right;// 指向当前结点右孩子
	BTDataType data;// 当前结点值域

	struct BinTreeNode* parent;//指向当前结点的双亲

}

2.5二叉树的遍历

这里的遍历就需要用到大量的递归了 

递归是个难点,学习递归就要弄懂两件事:当前问题,子问题

所谓递归,不过是一堆相同步骤的叠加,用很短的代码来处理相同的步骤。

写代码时需要把握好递归的截止条件、每一步递归进行的相同的步骤

二叉树的前、中、后序遍历常用的就是递归方式,当然也可以写非递归方式(这里常用栈的结构、循环来解决) 

2.5.1前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

前序遍历的过程中,所有节点的访问顺序都是根、左子树、右子树

下面这幅图表示的很形象

代码表示 
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}//结束条件
	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);//相同的每一步

	
}

 2.5.2中序遍历

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

中序遍历的过程中,所有节点的访问顺序都是左子树、根、右子树 

代码表示 
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);
}

2.5.3后续遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。  

后序遍历的过程中,所有节点的访问顺序都是左子树、右子树、根 

代码表示 
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%c ", root->_data);

}

 2.5.4层序遍历 

层序遍历是最符合人类习惯的遍历方式 

设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第3层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。  

  

代码表示

层序遍历的基础要用到数据结构队列, 先让第一个节点入队,后续按照:一个节点出队,该节点的子节点入队,这一方式进行循环遍历

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		
		printf("%c ", front->_data);

		QueuePop(&q);
		if(front->_left!=NULL)
			QueuePush(&q, front->_left);
		if (front->_right != NULL)
			QueuePush(&q, front->_right);
	}
	printf("\n");

}

总结示例 

  • 前序遍历结果:1 2 3 4 5 6
  • 中序遍历结果:3 2 1 5 4 6
  • 后序遍历结果:3 2 5 6 4 1  
  • 层序遍历结果:1 2 4 3 5 6 

2.6二叉树的其他代码 

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->_data == x)
	{
		return root;
	}
	BTNode* ret1 = BinaryTreeFind(root->_left,x);
	if (ret1)
	{
		return ret1;
	}
	BTNode* ret2 = BinaryTreeFind(root->_left, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL;
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	int left = BinaryTreeLevelKSize(root->_left, k - 1);
	int right = BinaryTreeLevelKSize(root->_right, k - 1);
	return left + right;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->_left == NULL && root->_right == NULL)
	{
		return 1;
	}

	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right) ;

}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{

	return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

---------------------------------------------------------------------------------------------------------------------------------

好啦二叉树的相关内容就先到这啦,后续应该会有所补充,敬请期待~

看到这里的小伙伴,求求点赞、关注、收藏~

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

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

相关文章

【昇腾AI创新大赛集训营南京站学习笔记】-Ascend算子开发课程

昇腾AI创新大赛训练营 14:00-14:30 基础知识-理论课 一、CANN 、达芬奇架构和算子 1.AI Core逻辑架构 达芬奇架构包含三部分&#xff1a; 1&#xff09;计算类&#xff1a;矩阵计算单元&#xff08;两个矩阵扔进去相乘&#xff09;、向量计算单元、标量计算单元 2&#xff09;控…

一天搞定React(4)——Redux

Hello&#xff01;大家好&#xff0c;今天带来的是React前端JS库的学习&#xff0c;课程来自黑马的往期课程&#xff0c;具体连接地址我也没有找到&#xff0c;大家可以广搜巡查一下&#xff0c;但是总体来说&#xff0c;这套课程教学质量非常高&#xff0c;每个知识点都有一个…

鸿蒙OpenHarmony Native API【drawing_path.h】 头文件

drawing_path.h Overview Related Modules: [Drawing] Description: 文件中定义了与自定义路径相关的功能函数 Since: 8 Version: 1.0 Summary Functions FunctionDescription[OH_Drawing_PathCreate] (void)[OH_Drawing_Path] * 函数用于创建一个路径对象OH_Drawin…

前端页面:用户交互持续时间跟踪(duration)user-interaction-tracker

引言 在用户至上的时代&#xff0c;精准把握用户行为已成为产品优化的关键。本文将详细介绍 user-interaction-tracker 库&#xff0c;它提供了一种高效的解决方案&#xff0c;用于跟踪用户交互的持续时间&#xff0c;并提升项目埋点的效率。通过本文&#xff0c;你将了解到如…

EXO-chatgpt_api 解释

目录 chatgpt_api 解释 resolve_tinygrad_tokenizer 函数 resolve_tokenizer 函数 调试和日志记录​​​​​​​ 参数 返回值 初始化方法 __init__ 异步方法 注意事项 chatgpt_api 解释 展示了如何在一个项目中组织和导入各种库、模块和类,以及如何进行一些基本的We…

双向链表(C语言版)

1. 双向链表的结构 注意&#xff1a;这里的“带头”跟单链表的“头结点”是两个概念&#xff0c;实际上在单链表阶段称呼不太严谨&#xff0c;但是为了更好地理解就直接称为单链表的头结点。带头链表里的头结点&#xff0c;实际为“哨兵位”&#xff0c;哨兵位结点不存储任何有…

rsync文件远程同步

目录 一、什么是rsync远程同步 二、实操rsync远程文件同步 1、配置rsync同步源 2、客户端部署 3、增量备份​编辑 4、删除文件 5、如何实现免交互登录 6、crontab rsync 实现定时同步 7、使用ssh实现rsync数据同步【☆】 如何使用ssh免交互实现数据同步&#xff1f;…

C++ Map Set的模拟实现

C Map Set的模拟实现 文章目录 前言一、Map 和 Set是什么&#xff1f;1.Set2.Map 二、困难点困难一、set和map中值的类型不同困难二、Map和Set中值不可修改困难三、红黑树中迭代器的和--1.2.- - 困难四、map中[ ] 运算符重载的实现1.修改红黑树以及Map和Set中insert的返回值1.修…

Spring Boot入门指南:轻松构建高效Spring应用(四)

Spring Boot入门指南&#xff1a;轻松构建高效Spring应用&#xff08;三&#xff09;-CSDN博客 一.传递参数 7.传递数组 1.在IDEA中写出处理代码 2.打开postman 或者这样:key相同时&#xff0c;也会组成一个数组。 8. 传递集合 如果像数组传参一样传递集合&#xff0c;可能…

Transformer合集

资料 位置编码&#xff1a;https://zhuanlan.zhihu.com/p/454482273 自注意力&#xff1a;https://zhuanlan.zhihu.com/p/455399791 LN&#xff1a;https://zhuanlan.zhihu.com/p/456863215 ResNet&#xff1a;https://zhuanlan.zhihu.com/p/459065530 Subword Tokenizati…

重生之我们在ES顶端相遇第5章-常用字段类型

思维导图 前置 在第4章&#xff0c;我们提到了 keyword&#xff08;一笔带过&#xff09;。在本章&#xff0c;我们将介绍 ES 的字段类型。全面的带大家了解 ES 各个字段类型的使用场景。 字段类型 ES 支持以下字段类型&#xff08;仅介绍开发中常用&#xff0c;更多内容请自…

接口自动化测试框架实战-4-日志方法封装

上一小节我们讲解了文件读写方法的封装方法,本小节我们完成一下框架日志的封装方法。 首先我们讲解一下日志封装和日志记录有哪些用处? 更加方便的设置日志的格式和输出方式全局方法可以各个类/函数中都能统一调用分类记录接口用例执行日志,方便嵌入测试报告错误日志提示,…

乘云数字受邀Zabbix MeetUp济南站,分享《DataBuff在打造可观测性数据底座上的探索》

7月20日&#xff0c;Zabbix主办的MeetUp线下活动在济南圆满举行&#xff0c;众多技术大咖汇集现场&#xff0c;交流技术知识、分享先进的思想。乘云数字受邀参加此次盛宴&#xff0c;创始人兼CEO向成钢在现场发表了关于“DataBuff在打造可观测性数据底座上的探索”的主题演讲。…

【多模态】CLIP-KD: An Empirical Study of CLIP Model Distillation

论文&#xff1a;CLIP-KD: An Empirical Study of CLIP Model Distillation 链接&#xff1a;https://arxiv.org/pdf/2307.12732 CVPR 2024 Introduction Motivation&#xff1a;使用大的Teacher CLIP模型有监督蒸馏小CLIP模型&#xff0c;出发点基于在资源受限的应用中&…

NFTScan 浏览器现已支持 .mint 域名搜索功能!

近日&#xff0c;NFT 数据基础设施 NFTScan 浏览器现已支持用户输入 .mint 域名进行 Mint Blockchain 网络钱包地址的搜索查询&#xff0c; NFTScan 用户能够轻松地使用域名追踪 NFT 交易&#xff0c;为 NFT 钱包地址相关的搜索查询功能增加透明度和便利性。 NFTScan explorer…

C++树形结构(2 树的直径)

目录 1.定义&#xff1a; 2.直径的性质&#xff1a; 3.树的直径求解方法&#xff1a; 4.直径端点求解方法&#xff1a; 朴素方法&#xff1a; 优化方法&#xff1a; 5.例题&#xff1a; 6.直径公共点&#xff1a; 7.例题&#xff1a; 8.去掉再加上&#xff1a; 9.例…

Hi3751V560_SELinux

Hi3751V560_SELinux setenforce Enforcing setenforce Permissive(或“setenforce 0”) getenforce V560:demo本身的: [ 13.765161] type=1400 audit(1628821512.905:4): avc: denied { read } for pid=1926 comm="system_server" name="ifindex" d…

vue3前端开发-小兔鲜项目-图片切换效果和动态class

vue3前端开发-小兔鲜项目-图片切换效果和动态class!这次实现的效果是&#xff0c;图片预览效果&#xff0c;根据小图片&#xff0c;来实时改变大图&#xff08;预览&#xff09;的效果。同时让动态的特征class也跟着显示出来。 <script setup> import {ref} from vue // …

【Vue3】响应式数据

【Vue3】响应式数据 背景简介开发环境开发步骤及源码使用 ref 定义基本类型响应式数据使用 reactive 定义对象类型响应式数据使用 ref 定义对象类型响应式数据 ref 和 reactive 的对比使用原则建议 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈…

【C++初阶】string类

【C初阶】string类 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 1.2 实际中 2. 标准库中的string类 2.1 string类 2.…