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

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

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

所属专栏:数据结构与算法
贝蒂的主页: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);
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式 六大原则之开放封闭原则

文章目录 定义理解 小结 定义 开闭原则规定软件中的对象、类、模块和函数对扩展应该是开放的&#xff0c;但对于修改是封闭的。这意味着应该用抽象定义结构&#xff0c;用具体实现扩展细节&#xff0c;以此确保软件系统开发和维护过程的可靠性。 理解 怎么理解这个呢&#x…

pycharm 里面安装 codeium 插件的时候,不能够弹出登录界面

pycharm 里面安装 codeium 插件的时候&#xff0c;不能够弹出登录界面 pycharm 里面安装 codeium 插件的时候&#xff0c;不能够弹出登录界面--解决如下A pycharm 里面安装 codeium 插件的时候&#xff0c;不能够弹出登录界面–解决如下 #踩坑/pycharm/codeium插件无法登录 安…

AI预测福彩3D采取887定位策略+杀断组+杀和尾+杀和值012缩水测试5月12日预测第1弹

前段时间工作太忙&#xff0c;手头上各种事情较多&#xff0c;没有静下心来对我的AI模型预测结果进行进一步分析筛选&#xff0c;导致最近连续几期与实际开奖结果相差较大。当然&#xff0c;客观来说&#xff0c;搞6码定位的确难度比较大&#xff0c;昨天跟几个常年研究3D的彩友…

计算机毕业设计springboot+vue高校教师职称评审评定系统605z3

技术栈 前端&#xff1a;vue.jsElementUI 开发工具&#xff1a;IDEA 或者eclipse都支持 编程语言: java 框架&#xff1a; ssm/springboot 数据库: mysql 版本不限 数据库工具&#xff1a;Navicat/SQLyog都可以 详细技术&#xff1a;javaspringbootvueMYSQLMAVEN 本系统采用in…

Golang 开发实战day13 - Reciver Functions

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 Golang 开发实战day13 - 接收…

【linux】linux工具使用

这一章完全可以和前两篇文件归类在一起&#xff0c;可以选择放一起看哦 http://t.csdnimg.cn/aNaAg http://t.csdnimg.cn/gkJx7 拖更好久了&#xff0c;抱歉&#xff0c;让我偷了会懒 1. 自动化构建工具 make , makefile make 是一个命令&#xff0c;makefile 是一个文件&…

阿里开源编程大模型 CodeQwen1.5:64K92编程语言,Code和SQL编程,评测接近GPT-4-Turbo

前言 阿里巴巴最近发布的CodeQwen1.5模型标志着其在编程语言模型领域的一次重大突破。这款开源模型不仅支持高达92种编程语言和64K的上下文长度&#xff0c;而且在多项性能评测中显示出接近或超过当前行业领导者GPT-4-Turbo的能力。 Huggingface模型下载&#xff1a;https://h…

网络层(计算机网络谢希仁第八版)——学习笔记四

关于网络层的争论 争论的内容&#xff1a;网络层应该想运输层提供正阳的服务&#xff0c;是“面向连接的”还是“无连接”。 其实质就是&#xff1a;可靠交付应该交给谁负责&#xff1f;面向连接表示网络层负责可靠交付&#xff0c;无连接则是把这个任务交给运输层。 让网络层负…

【图像识别】Swin Transformer

一、引言 论文&#xff1a; Swin Transformer: Hierarchical Vision Transformer using Shifted Windows 作者&#xff1a; Microsoft Research Asia 代码&#xff1a; Swin Transformer 特点&#xff1a; 提出滑动窗口自注意力 (Shifted Window based Self-Attention) 解决Vi…

【中航证券军工】北摩高科2023年报2024Q1点评:聚焦航空及军工主赛道,民机业务有望成为第二曲线

事件 公司4月24日公告&#xff0c;2024Q1实现营收&#xff08;2.40亿元&#xff0c;同比-23.71%)&#xff0c;归母净利润&#xff08;0.73亿元&#xff0c;同比-45.63%)&#xff0c;毛利率&#xff08;62.63%&#xff0c;同比-7.22pcts)&#xff0c;净利率&#xff08;37.34%&…

【SpringBoot记录】从基本使用案例入手了解SpringBoot-数据访问(1)

前言 在程序开发尤其是网页应用开发中&#xff0c;数据访问是必不可少的。通过前面的基本案例我们完成了一个简单的SpringBoot Web应用并对自动配置原理有了一定了解&#xff0c;本节在上述案例基础上&#xff0c;继续编写数据访问案例&#xff0c;将通过SpringBoot中数据访问…

手机同步与数据安全:让手机和电脑完美结合!

在当今这个高度信息化的社会&#xff0c;手机和电脑不仅为我们提供了丰富的信息资源&#xff0c;让我们能够随时随地获取所需的信息&#xff0c;还为我们的生活带来了极大的便利。无论是工作、学习还是娱乐&#xff0c;手机和电脑都发挥着至关重要的作用。 然而&#xff0c;随…

华为OD机试 - 执行任务赚积分 - 动态规划(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷+B卷+C卷)》。 刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。 一、题目描述 现有 N 个任…

Oracle11g账户频繁被锁定的3种解决办法

方法1&#xff1a;创建触发器 方法1&#xff1a;数据库中创建触发器(只记录失败)&#xff0c;但是需要开发同意或者开发自己创建。找到密码输入错误的服务器&#xff0c;进行数据源配置的更改。 该方法适用于要求找到密码错误用户所在服务器的场景下。 CREATE OR REPLACE TR…

在另外一个页面,让另外一个页面弹框显示操作(调佣公共的弹框)

大概意思是&#xff0c;登录弹框在另外一个页面中&#xff0c;而当前页面不存在&#xff0c;在当前页面中判断如果token不存在&#xff0c;就弹框出登录的弹框 最后一行 window.location.href … 如果当前用户已登录&#xff0c;则执行后续操作(注意此处&#xff0c;可不要)

Jboss 反序列化 CVE-2017-12149

一、漏洞简介 JBoss是一个管理EJB的容器和服务器&#xff0c;支持EJB 1.1、EJB 2.0和EJB3的规范。在/invoker/readonly路径下&#xff0c;攻击者可以构造序列化代码传入服务器进行反序列化,由于没有对反序列化操作进行任何检测&#xff0c;导致攻击者可以执行任意代码。 而jbo…

AI 重塑产品设计

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

【二叉树】Leetcode N 叉树的层序遍历

题目讲解 429. N 叉树的层序遍历 算法讲解 在做层序遍历的时候由于它的每一个结点是有val vector child组成&#xff0c;所以在做层序遍历的时候需要考虑它每一层结点的个数&#xff0c;那我们就可以使用一个queue保存每一层的结点&#xff1b;那么我们在做第一层的时候&am…

【GD32F470紫藤派使用手册】第八讲 ADC-规则组多通道采样实验

8.1 实验内容 通过本实验主要学习以下内容&#xff1a; ADC的简介 GD32F470 ADC工作原理 DMA原理 规则组多通道循环采样 8.2 实验原理 8.2.1 ADC原理 我们知道&#xff0c;自然界中有非常多的模拟信号&#xff0c;比如光照强度&#xff0c;还有其他的例如温度、声音等等…

【C++】stack和queue 适配器

&#x1f525;个人主页&#xff1a;北辰水墨 &#x1f525;专栏&#xff1a;C学习仓 本节内容我们来讲解栈和队列的模拟实现&#xff0c;文末会赋上模拟实现的代码 一、stack的使用和模拟实现 stack适配器的介绍&#xff1a; 1. stack是一种容器适配器&#xff0c;专门用在具…