探索数据结构:树与二叉树

news2024/11/24 8:53:02

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty’s blog

1. 树

1.1. 树的定义

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

img

在树中有一个特殊的结点,称为根结点,根节点没有前驱结点

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

img

注意:树形结构中,子树之间不能有交集,否则就不是树形结构

img

1.2. 树的基本概念

术语定义
节点的度一个节点含有的子树的个数称为该节点的度,比如说节点1的度为2
叶节点度为0的节点,比如说4,5,6节点
分支节点度不为0的节点,比如说2,3节点
双亲节点若一个节点含有子节点,则这个节点称为其子节点的父节点。比如说2是4,5的双亲节点
子节点一个节点含有的子树的根节点称为该节点的子节点,比如说4,5是2的子节点
兄弟节点具有相同父节点的节点互称为兄弟节点,比如说4,5就是兄弟节点
树的度一棵树中,最大的节点的度称为树的度,比如说上面这棵树的度为2
节点的层次从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度树中节点的最大层次,比如说上面这棵树的高度为3
堂兄弟节点双亲在同一层的节点互为堂兄弟,比如说5,6节点
节点的祖先从根到该节点所经分支上的所有节点,比如说1就是所有节点的祖先
子孙以某节点为根的子树中任一节点都称为该节点的子孙,比如说所有节点都是1的子孙
森林由m(m>0)棵互不相交的树的集合称为森林

1.3. 树的表示方法

下面我们将采用三种方式表示下面这课树:

img

1.3.1. 双亲表示法

双亲表示法采用顺序表的方式存储,即用顺序表存储各个节点的数据,并且同时存储其双亲节点的下标。注意:根节点没有双亲节点,所以特别记为-1。

#define MAX_SIZE 10
typedef int DataType;
typedef struct Node
{
    DataType data;//数据域
    int parent; //双亲结点在数组中的位置下标
}Node;
typedef struct PTree
{
    //存放树中所有结点
    Node tnode[MAX_SIZE];
    //当前结点个数
    int n;
}PTree;

img

1.3.2. 树的孩子表示法

树的孩子表示法就是采用顺序表与链表结合的形式,用顺序表存储树的值与链表的头节点,而链表的头节点存储其孩子节点在顺序表中的下标,若没有则记为空(N)。

#define MAX_SIZE 10
#define DataType int
typedef struct ListNode 
{
    int child;
    struct ListNode* next;
}ListNode;
typedef struct TNode
{
    DataType data;
    //孩子链表的头指针
    ListNode* firstchild;
}TNode;
typedef struct PTree{
    //存储结点的数组
    TNode nodes[MAX_SIZE];
    int n; //结点数量
}PTree;

img

1.3.3. 左孩子右兄弟表示法

最常用表示树的的方法就是左孩子右兄弟表示法,即定义两个指针,让左指针指向子节点,右指针指向兄弟节点。

如果没有节点,则都指向空。

typedef int DataType;
struct Node
{
	struct Node* leftChild1; // 孩子结点
	struct Node* rightBrother; // 指向其下一个兄弟结点
	DataType _data; // 结点中的数据域
};

img

1.4. 树的实际应用

在linux环境下目录结构就是有一颗树构成,而在Windows环境下,目录许多内容并不交叉,所以是由森林构成。

img

2. 二叉树

2.1. 二叉树的定义

一棵二叉树是结点的一个有限集合,该集合可能为空,也可以由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

注意:

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

img

img

2.2. 特殊的二叉树

2.2.1. 满二叉树

满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是N=2k -1,则它就是满二叉树。

img

2.2.2. 完全二叉树

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

img

2.3. 二叉树的特点

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2i -1个结点。
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数N=1+2+4+…+2i -1=2h -1
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为N0, 度为2的分支结点个数为N2, 则有 N0=N2+1

证明如下:

假设节点总数为N,如果度为0其叶结点个数为N0, 度为1的分支结点个数为N1,度为2的分支结点个数为N2

节点总数:N=N0 + N1+ N2 又因为二叉树的边比节点树少1,所以N= N1 +2N2+1=>N0 + N1+ N2= N1 +2N2+1=>N0=N2+1

  1. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h =log2 (n+1) 。

  2. 对于具有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. 数组存储

我们可以通过双亲节点与子节点下标之间的映射关系将二叉树存储在数组中,如果节点为空,我们以INT_MAX来标记。

img

2.4.2. 链式存储

除了数组存储,我们也可以链式存储二叉树。

typedef struct BinaryTreeNode
{
    struct BinTreeNode* left; // 左孩子
    struct BinTreeNode* right; // 右孩子
    DataType data; // 当前节点值域
}BTree;

2.5. 二叉树的遍历

二叉树的遍历一般有四种方法:前序遍历,中序遍历,后序遍历,层序遍历。

img

2.5.1. 前序遍历

前序遍历:先遍历根节点,再依次遍历左子树,右子树。而遍历左子树,又要先遍历根节点,再依次遍历左子树,右子树…直至遍历到空树。

  1. 递归实现
void PreOrder(BTree*root)
{
    if (root == NULL)
    {
        return;
    }
    printf("%d ", root->data);//根节点
    PreOrder(root->left);//左子树
    PreOrder(root->right);//右子树
}
  1. 非递归实现

非递归实现树的前序遍历我们需要借助栈这个数据结构,来达到与递归一样的深度优先遍历的目的。并且栈中存储元素为BTree*

  typedef BTree* STDataType;
  void PreOrder(BTree* root)
  {
	  Stack s;				
	  InitStack(&s);			  
	  BTree*p = root;// p为遍历指针
	  while (p || !IsEmpty(&s))  // 栈不为空或p不为空时循环
	  {
		  if(p!=NULL)//入栈
		  {
			  printf("%d ", p->data);//根节点
			  StackPush(&s, p);
			  p = p->left;
		  }
		  else//出栈,访问右子树
		  {
			  p = StackTop(&s);//访问原本双亲节点
			  StackPop(&s);	  // 栈顶元素出栈
			  p = p->right; //访问
		  }					 
	  }
  }
2.5.2. 中序遍历

中序遍历:先遍历左子树,再依次遍历根节点,右子树。而遍历左子树,又要先遍历左子树,再依次遍历根节点,右子树…直至遍历到空树。

  1. 递归实现
void Inorder(BTree*root)
{
    if (root == NULL)
    {
        return;
    }
    PreOrder(root->left);//左子树
    printf("%d ", root->data);//根节点
    PreOrder(root->right);//右子树
}
  1. 非递归实现

非递归实现树的中序遍历我们需要借助栈这个数据结构,来达到与递归一样的深度优先遍历的目的。并且栈中存储元素为BTree*

  typedef BTree* STDataType;
  void Inorder(BTree* root)
  {
	  Stack s;
	  InitStack(&s);
	  BTree* p = root;// p为遍历指针
	  while (p || !IsEmpty(&s))  // 栈不为空或p不为空时循环
	  {
		  if (p != NULL)//入栈
		  {
			  StackPush(&s, p);
			  p = p->left;
		  }
		  else//出栈,访问右子树
		  {
			  p = StackTop(&s);//访问原本双亲节点
			  StackPop(&s);	  // 栈顶元素出栈
			  printf("%d ", p->data);
			  p = p->right; //访问
		  }
	  }
  }
2.5.3. 后序遍历

后序遍历:先遍历左子树,再依次遍历右子树,根节点。而遍历左子树,又要先遍历左子树,再依次遍历右子树,根节点…直至遍历到空树。

  1. 递归实现
void Postorder(BTree*root)
{
    if (root == NULL)
    {
        return;
    }
    PreOrder(root->left);//左子树
    PreOrder(root->right);//右子树
    printf("%d ", root->data);//根节点
}
  1. 非递归实现

非递归实现树的后序遍历我们需要借助栈这个数据结构,来达到与递归一样的深度优先遍历的目的。并且栈中存储元素为BTree*

void Postorder(BTree* root)
{
	Stack s;
	InitStack(&s);
	BTree* p = root;// p为遍历指针
	BTree* v = root;// v标记已访问节点
	while (p || !IsEmpty(&s))  // 栈不为空或p不为空时循环
	{
		while(p != NULL)//入栈
		{
			StackPush(&s, p);
			p = p->left;
		}
		p = StackTop(&s);//访问双亲节点
		if (p->right && p->right != v)//存在右子树,且没有被访问
		{
			p = p->right;//访问
		}
		else//没有右子树或者右子树已被访问
		{
			printf("%d ", p->data);
			v = p;//记录当前访问的节点
			p = NULL;//防止重复访问左子树
			StackPop(&s);// 栈顶元素出栈
		}
	}
}
2.5.4. 层序遍历

img

层序遍历顾名思义就是一层一层地遍历,这时就需要借助一个数据结构:队列来辅助实现。

void leverOrder(BTree* root, Queue* pq)
{
	if (root == NULL)//为空直接返回
	{
		return;
	}
	QueuePush(pq, root);//插入第一个节点
	while (!QueueEmpty(pq))//队列不为空
	{
		BTree* p = QueueFront(pq);
		printf("%d ", p->val);
		QueuePop(pq);
		if (p->left != NULL)//带入左孩子
		{
			QueuePush(pq, p->left);
		}
		if (p->right != NULL)//带入右孩子
		{
			QueuePush(pq, p->right);
		}
	}
}

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

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

相关文章

nodejs复习笔记

最近在复习nodejs&#xff0c;整理了一些笔记来记录和分享。 非常惭愧&#xff0c;我之前关于nodejs学习的一篇文章《nodejs全栈开发学习笔记》已经是2019年6月份的时候了&#xff0c;大概浏览了一下&#xff0c;发现当时很多不明白的地方&#xff0c;现在通过复习&#xff0c…

【MySQL数据库开发设计规范】之表设计规范

欢迎点开这篇文章&#xff0c;自我介绍一下哈&#xff0c;本人姑苏老陈 &#xff0c;是一名JAVA开发老兵。 本文收录于 《MySQL数据库开发设计规范》专栏中&#xff0c;该专栏主要分享一些关于MySQL数据库开发设计相关的技术规范文章&#xff0c;定期更新&#xff0c;欢迎关注&…

Java反射(含静态代理模式、动态代理模式、类加载器以及JavaBean相关内容)

目录 1、什么是反射 2、Class类 3、通过Class类取得类信息/调用属性或方法 4、静态代理和动态代理 5.类加载器原理分析 6、JavaBean 1、什么是反射 Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得…

【计算机毕业设计】基于微信小程序文章管理系统

随着 计算机技术的成熟&#xff0c;互联网的建立&#xff0c;如今&#xff0c;PC平台上有许多关于文章管理方面的应用程序&#xff0c;但由于使用时间和地点上的限制&#xff0c;用户在使用上存在着种种不方便&#xff0c;而开发一款基于微信小程序的文章管理系统&#xff0c;能…

基于机器学习的网络流量识别分类(完整资源收米)NetFlow-Sentry

项目介绍 这是一个名为NetFlow-Sentry的项目&#xff0c;项目实现了对网络流量的实时检测&#xff0c;特征抓取&#xff0c;内置了基于Pytorch的深度学习模型来进行流量分类。该项目除可以进行实时特征提取&#xff0c;还提供了网络流量数据的输入口&#xff0c;可以直接识别分…

并发——进程

1. 程序 程序&#xff08;program&#xff09;是什么&#xff1f; 计算机程序&#xff08;computer program&#xff09;一般是指以某些程序设计语言编程&#xff0c;能够运行于某种目标体系结构上 程序 数据结构 算法 数据结构&#xff1a;用来表示人们思维对象的抽…

mysql的存储结构

一个表就是一个ibd文件 .ibd文件大小取决于数据和索引&#xff0c;在5.7之后才会为每个表生成一个独立表空间即一个ibd文件&#xff0c;在此之前&#xff0c;所有表默认下都会存储在“系统表空间”&#xff08;共享表空间&#xff09;&#xff0c;所有表都在一个ibd文件。 inn…

我的全新官网

科技语者-探索未来的语言和沟通 (chgskj.cn) 另外我还开放了一个网站科技语者-介绍页 (null.fit)

Verilog复习(四)| 组合逻辑

一位全加器结构描述&#xff1a; 数据流描述&#xff1a; 行为描述&#xff1a; 只要有事件发生&#xff08;列表中任何 信号有变化&#xff09;&#xff0c;就执行begin…end 的语句 。 always的事件控制方式 边沿触发 always (posedge clk) // clk从低电平->高&#x…

使用Flask构建POST请求的Web应用

文章目录 准备工作创建路由处理POST请求创建表单页面运行应用结论 在Web开发中&#xff0c;处理POST请求是一项常见任务&#xff0c;特别是在构建表单提交、用户注册和数据提交等功能时。Flask是一个简单而强大的Python Web框架&#xff0c;它提供了方便的工具来处理HTTP请求&a…

答辩PPT自述稿如何与幻灯片同步?笔灵AI一键生成功能解析

在我原本的认知里面&#xff0c;答辩PPT是要包含论文各个章节的&#xff0c;在答辩时需要方方面面都讲到的&#xff0c;什么摘要、文献综述、实证分析、研究结果样样不落。但是&#xff0c;这大错特错&#xff01; 答辩PPT环节时长一般不超过5分钟&#xff0c;老师想要的答辩P…

Postman工具介绍与安装

一、Postman介绍 Postman 乃是一款对 HTTP 协议予以支持的接口调试及测试工具&#xff0c;其突出特性在于功能强大&#xff0c;并且使用简便、易用性良好。不管是开发人员开展接口调试工作&#xff0c;还是测试人员进行接口测试任务&#xff0c;Postman 均属于首选工具之一。 接…

面试八股之Redis篇2——redis分布式锁

&#x1f308;hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;一名不断学习的码农&#xff0c;很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 &#x1f3c3;人生之义&#xff0c;在于追求&#xff0c;不在成败&#xff0c;勤通…

图书馆APP开发解决方案

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

blender 为世界环境添加纹理图像

1、打开世界环境配置项 2、点击颜色右侧的黄色小圆&#xff0c;选择环境纹理 3、打开一张天空图像 4、可以通过调整强度/力度&#xff0c;调整世界环境的亮度

ambari-server高可用配置方案

制品 https://kdocs.cn/l/cie4hSgvUunX 前置条件 环境需要支持VRRP协议 环境需要配置好yum源 变更影响面 变更不会影响其他组件 配置lb(需要客户侧配置并提供LB地址) 转发方式选择 主备 监听端口为8080、8440、8441 协议为tcp 后端监听选择kde-offline1为主 后端监听选择kde-…

QT-小项目:连接MY SQL数据库实现登录(下一章实现登录注册账号和忘记密码功能)

一、环境准备 1、下载MYSQL 64位&#xff0c;安装完成&#xff0c;制作简易数据库教程如下&#xff1a; MY SQL安装 2、QT 编译器使用 二、实现工程目录&#xff08;基于上一章基础上&#xff09; 三、源程序增加内容如下&#xff1a; login.cpp 增加头文件&#xff1a; #in…

MES系统与WMS集成方法(满分100学习资料)

导语 大家好&#xff0c;我是智能仓储物流技术研习社的社长&#xff0c;老K。专注分享智能仓储物流技术、智能制造等内容。 新书《智能物流系统构成与技术实践》 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载 这份文件是关于MES系…

头歌C语言数据结构(队列的应用)

第1关&#xff1a;循环队列 任务描述 本关任务&#xff1a;编写一个循环队列&#xff0c;实现入队、出队操作&#xff0c;判断队空、队满等特殊情况。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.循环队列定义&#xff0c;2.入队、出队的定义&#xff…

别的项目都有 awesome 仓库,RT-Thread 也要有!

awesome 仓库是 GitHub 上用于收集某个项目或者某个语言相关的优质内容的仓库&#xff0c;例如中间件、新闻资讯、网站等。 作为 RT-Thread 开发者&#xff0c;看到别的项目都有 awesome 仓库&#xff0c;我想 RT-Thread 也应该有&#xff01; 于是&#xff0c;我创建一个 aw…