树及其遍历

news2024/11/15 11:20:51

文章目录

    • 树定义
    • 专业术语
    • 树分类
  • 二叉树
    • 分类
    • 存储
      • 连续存储(完全二叉树)
      • 链式存储
      • 一般树的存储
      • 森林的存储
    • 线索二叉树
    • 哈夫曼树
      • 构造步骤
    • 遍历
      • 先序遍历
      • 中序遍历
      • 后续遍历
    • 链式二叉树遍历具体代码
    • 已知两种遍历序列求原始二叉树
      • 已知先序和中序求后序
      • 已知中序和后序求先序
      • 已知先序和后序求中序
  • 树的应用

树定义

像这种有层次关系进行存储的,就是一棵树,是非线性结构。
在这里插入图片描述
以递归的方式进行定义。

专业定义:

  • 有且只有一个称为根的节点。
  • 有若干个互不相交的子树,这些子树本身也是一棵树。

通俗定义:

  • 树是由节点和边组成。
  • 每一个节点节点只有一个父节点但可以有多个子节点。
  • 有一个节点例外,该节点没有父节点,此节点称为根节点。

专业术语

  • 节点:就是一个个的圈。
  • 父节点:和当前节点上一个紧挨着的节点。
  • 子节点:父节点下连着的子节点。
  • 子孙:父节点下的所有节点。
  • 深度:从根节点到最底层的层数称为深度。
  • 叶子节点:没有子节点的节点。
  • 非终端节点:实际上就是非叶子节点,有子节点的节点。
  • 根节点:看有没有子节点。
  • 度:子节点的个数称为度。

树分类

  • 一般树:任意一个节点的子节点的个数都不受限制
  • 二叉树:任意一个节点的子节点个数最多两个,且子节点的位置不可更改。
  • 森林:有互不相交的,一般的树合在一起,就是森林。

二叉树

分类

  • 一般二叉树
  • 满二叉树:在不增加树的层数的前提下,无法再多添加一个节点的二叉树就是满二叉树。
  • 完全二叉树:如果只是删除了满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全的二叉树。
    满二叉树是完全二叉树的一个特例。

存储

连续存储(完全二叉树)

要把一颗一般的二叉树以数组方式存储的话,必须先把这个一般的二叉树转化为完全二叉树。

eg:
在这里插入图片描述
首先这不是一个完全二叉树,因为完全二叉树先是一个满的二叉树,后来再在最底一层砍。所以要使用连续存储就必须先把这个二叉树变成完全二叉树。

在这里插入图片描述
先变成满二叉树,再把最底层的最右边得到删掉,就成了完全二叉树。
在这里插入图片描述
黄色线框的可以不保存。(先转化成满二叉树,再把最后一层最右边开始的点删掉,就是完全二叉树)红色的是有效节点,别人是无法通过零散的红色有效节点还原二叉树的本来面目的。(排序通过先中后进行排序。)
树是非线性的,将非线性的数转换成线性结构的,结果是不知道的。
数组只能以完全二叉树的方式进行存储,即不能只存放有效节点,因为通过先中后进行排序后,还原不了其本来的面目。所以把所有点都存进去,才好还原。

优点:查找某个节点的父节点和子节点很快。
缺点:耗用内存空间过大。

链式存储

通过指针域弄成一个连续的存储。

一般树的存储

  1. 双亲表示法:求父节点很方便,因为跟的是下标。
  2. 孩子表示法:求子节点方便,跟着的是子节点。
  3. 双亲孩子表示法:有链表,有数组,有下标,指针域,求父节点与子节点都很方便,就是代码复杂。
  4. 二叉树表示法:把一个普通树转化成二叉树来存储,具体转换方法是,设法保证任意一个节点左指针域指向它的第一个孩子,右指针域指向它的下一个兄弟节点,只要能满足此条件,就能把一个普通的数转化为二叉树。(转化时,只有左边的孩子,右边都是与孩子并列的兄弟,即一个普通的数转化为二叉树就没有右子树。例如下方的2,3,4,5,真正是孩子的只有2,然后3,4,5都是与其并列的项,排在右边。3也没有左兄弟,所以把4排在右边,5就同理了。)
    eg如下,将一个普通的树转化为二叉树。
    在这里插入图片描述

森林的存储

几个树互不相交,就组成了一个森林。
在这里插入图片描述
先把森林转化为一个二叉树,再进行存储。而森林的二叉树存储规则为:把B当A的兄弟,把G当B的兄弟。
在这里插入图片描述
森林转成二叉树的步骤与之前一般树转化为二叉树也是一致的。

线索二叉树

当用二叉链表作为二叉树的存储结构时,可以很方便地找到某个结点的左右孩子;但一般情况下,无法直接找到该结点在某种遍历序列中的前驱和后继结点。
在这里插入图片描述
因为还剩下了n+1个指针域,将其利用起来,存储前驱后继指针的地址,称此为线索。
在这里插入图片描述
在这里插入图片描述
可以用ltag与rtag的方式来区分是否为孩子还是线索。(左孩子与前驱,右孩子与后继)

//线索二叉树的结点结构
typedef struct BiThrNode{
	int data;
	int ltag, rtag;
	struct BiThrNode *lchild, rchild;
}BiThrNode, *BiThrTree;

在这里插入图片描述
在这里插入图片描述
因为首尾还有两个指针域悬空,可以增加一个头结点,让其指向头结点。
在这里插入图片描述

哈夫曼树

在这里插入图片描述
相当于把一个带权重的树进行重排,排成一个考虑权重最优的二叉树。

构造步骤

在这里插入图片描述
在这里插入图片描述
两个权重相同的直接连接,权重不相同的按照权重小的为左孩子,权重大的为右孩子的原则,进行排列。

遍历

把非线性的树转化成非线性的序列。

先序遍历

先访问根节点,再先序访问左子树,再先序访问右子树。假设二叉树如下:
在这里插入图片描述

运用递归的思想,先访问根节点A,再先序访问左子树,再先序访问右子树。在访问左子树的时候,因为左子树也是一棵树,所以会又绕到了先访问根节点B,再顺着来看左子树与右子树,即B的左子树为D,B没有右子树,因为D没有左子树以及右子树,为空,则此次递归结束,BD访问完毕;紧接着先序遍历A的右子树,右子树是个二叉树,再从根节点开始,再遍历左子树,再遍历右子树。都访问完毕之后,才是访问完毕。
eg:
在这里插入图片描述
最后的遍历节点就是ABCDEFLQMNS。

中序遍历

中序遍历左子树,再访问根节点,再中序遍历右子树。
在这里插入图片描述
先中序遍历左子树,B的左子树为空,所以先访问左子树的根节点,即为B,所以先B,B访问完访问B的右子树,也是非空的,所以递归思想,B的右子树先去中序遍历左子树,B的左子树为C的左子树,所以先访问D,再访问根节点C,再访问E。
最后顺序为BDCEALFNQM

eg:
在这里插入图片描述

遍历顺序:BDCAMQELN

后续遍历

中序遍历左子树,中序遍历右子树,再访问根节点。遍历都是以根节点的顺序来判断的。
eg:
在这里插入图片描述

遍历顺序:BDMFLECA(先全部的左,再全部的右,最后根)

在这里插入图片描述

遍历顺序:NWTSFPLQM

链式二叉树遍历具体代码

# include <stdio.h>
# include <malloc.h>

struct BTNode
{
	char data;
	struct BTNode * pLchild; 
	struct BTNode * pRchild;
};

void PostTraverseBTree(struct BTNode * pT);
struct BTNode * CreateBTree(void);
void PreTraverseBTree(struct BTNode * pT);
void InTraverseBTree(struct BTNode * pT);

int main(void)
{
	struct BTNode * pT = CreateBTree();
	
//	PreTraverseBTree(pT);
//	InTraverseBTree(pT);
	PostTraverseBTree(pT);
	
	return 0;
}

void PostTraverseBTree(struct BTNode * pT)
{
	if (NULL != pT)
	{
		if (NULL != pT->pLchild)
		{
			PostTraverseBTree(pT->pLchild);
		}	
		if (NULL != pT->pRchild)
		{
				PostTraverseBTree(pT->pRchild);
		}
		printf("%c\n", pT->data);
	}
}

void InTraverseBTree(struct BTNode * pT)
{
	if (NULL != pT)
	{
		if (NULL != pT->pLchild)
		{
			InTraverseBTree(pT->pLchild);
		}
		
		printf("%c\n", pT->data);
	
		if (NULL != pT->pRchild)
		{
				InTraverseBTree(pT->pRchild);
		}	
	}
}

void PreTraverseBTree(struct BTNode * pT)
{
	if (NULL != pT)
	{
		printf("%c\n", pT->data);
	
		if (NULL != pT->pLchild)
		{
			PreTraverseBTree(pT->pLchild);
		}
		
		if (NULL != pT->pRchild)
		{
				PreTraverseBTree(pT->pRchild);
		}	
	}	
}

struct BTNode * CreateBTree(void)
{
	struct BTNode * pA = (struct BTNode *)malloc(sizeof(struct BTNode));
	struct BTNode * pB = (struct BTNode *)malloc(sizeof(struct BTNode));
	struct BTNode * pC = (struct BTNode *)malloc(sizeof(struct BTNode));
	struct BTNode * pD = (struct BTNode *)malloc(sizeof(struct BTNode));
	struct BTNode * pE = (struct BTNode *)malloc(sizeof(struct BTNode));

	pA->data = 'A';
	pB->data = 'B';
	pC->data = 'C';
	pD->data = 'D';
	pE->data = 'E';

	pA->pLchild = pB;
	pA->pRchild = pC;
	pB->pLchild = pB->pRchild = NULL;
	pC->pLchild = pD;
	pC->pRchild = NULL;
	pD->pLchild = NULL;
	pD->pRchild = pE;
	pE->pLchild = pE->pRchild = NULL;

	return pA;
}

已知两种遍历序列求原始二叉树

单纯的知道先中后三种序列当中的任何一个,都不能把原始的二叉树序列还原回来,当已知两种序列,可以推出原始的二叉树。

已知先序和中序求后序

示例1:
先序:ABCDEFGH
中序:BDCEAFHG
求后序:因为根据先序,第一个访问的一定是根节点,所以根节点为A,再中序,中间的A确定是根节点,A旁边的是左子树,A右边的是右子树。确定了左子树BDCE与右子树FHG,现在分别找左右子树的根节点,由先序序列可知,先出现的肯定为根。之后确定根节点为BF,之后确定下一个根节点为C,因为中序先遍历左子树,所以D是C的左子树,E就是C的右子树,至此,A的左子树全部推完。右子树类似,推完F,发现在中序序列当中F左边没有点了,说明F没有左子树,只有右子树G,G的左边有H,所以H是G的左子树。
所以后序序列就是DECBHGFA
在这里插入图片描述
示例2:
先序:ABDGHCEFI
中序:GDHBAECIF
求后序:根节点为A,A的左子树为GDHB,右子树为ECIF,左子树当中,第一个根节点为B,B的左子树为GDH,D又为根节点,所以GH为其两树,G为左子树,H为右子树,至此A的左子树完全推出;A的右子树为ECIF,在ECIF当中,C为根节点,C的左子树为E,C的右子树为F,F的左子树为I,至此,A的右子树完全推出。
在这里插入图片描述
后序即为:GHDBIEFCA

已知中序和后序求先序

中序:BDCEAFHG
后序:DECBHGFA
求先序:根节点为A,最后出现的后序节点是根节点。所以BDCE是左子树,FHG是右子树。F是根节点,B也是根节点,因为在各个组合中最后出现(依照后序)再在中序中查找,B没有左子树,只有右子树DCE,C在后序最后出现,是根(哪一个在后序最后出现,哪一个就是根),所以C又有左子树又有右子树,左子树为D,右子树为E,至此A的左子树全部推完;A的右子树是FHG,F是根节点,F只有右子树HG,根节点为G,G有左子树H,至此,A的右子树全部推完。
在这里插入图片描述
先序:ABCDEFGH

已知先序和后序求中序

和以上两种情况类似。

树的应用

  • 树是数据库中数据组织的一种重要形式
  • OS当中的子父进程的关系也是树
  • 面向对象语言中类的继承关系
  • 哈夫曼树
  • B树
  • B+树
  • B*树
  • 红黑树
  • AVL树

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

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

相关文章

mtk桌面壁纸滚动

路径&#xff1a;packages\apps\Launcher3\src\com\android\launcher3 目前代码中是未打开的状态 packages\apps\Launcher3\src\com\android\launcher3\util\WallpaperOffsetInterpolator.java 涉及到的方法有&#xff1a;&#xff08;重要性从上往下&#xff09; updateOffs…

架构的分类

目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 ​二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…

Vue2 第四节 计算属性,监视属性

1.计算属性 2.监视属性 3.计算属性与监视属性之间的关系 一.计算属性 定义&#xff1a;要用的属性不存在&#xff0c;要通过已有属性计算得来原理&#xff1a;底层借助了Object.defineproperty方法提供的getter和setterget函数什么时候会执行&#xff1a;初次读取的时候会执…

银河麒麟安装solr及配置文件修改

下载并解压 将从官网下载的solr.tar.gz解压到目标文件夹 解压命令&#xff1a;tar -zxvf solr-xxx.tar.gz 修改配置文件 进入目录&#xff1a; /home/solr/solr-8.5.1/bin/init.d 修改solr文件里面部分配置 修改solr服务路径 SOLR_INSTALL_DIR"/opt/solr" 改为…

app稳定性测试-iOS篇

稳定性测试&#xff1a;测试应用程序在长时间运行过程中是否存在内存泄漏、崩溃等问题&#xff0c;以确保应用程序具有较高的稳定性和可靠性。 对于安卓端&#xff0c;官方提供了很好的稳定性测试工具&#xff1a;monkey。 相比较而言&#xff0c;iOS则没有&#xff0c;而且当前…

#systemverilog# 说说Systemverilog中《automatic》那些事儿(关于for循环内置变量研究)

#systemverilog# 说说Systemverilog中《static》那些事儿(理论篇1) 上面的截图,是来自于前面的文章,当时留下的疑问,今天和大家是实践一下小细节问题。 一 for 内置声明变量修饰 1.1 static 修饰

第八章浮点单元(Cortex-M7 Processor)

目录 浮点单元 8.1关于FPU 8.2FPU功能描述 8.2.1操作方式 8.2.2符合IEEE 754标准 8.2.3异常 8.3FPU编程器模型 浮点单元 介绍FPU (Floating Point Unit)。 它包含以下部分: 关于FPU的信息见8-2。FPU功能描述见8-3。FPU程序员模型在第8-5页。 8.1关于FPU Cortex-M7处…

基于RK3588+FPGA+AI算法定制的智慧交通与智能安防解决方案

随着物联网、大数据、人工智能等技术的快速发展&#xff0c;边缘计算已成为当前信息技术领域的一个热门话题。在物联网领域&#xff0c;边缘计算被广泛应用于智慧交通、智能安防、工业等多个领域。因此&#xff0c;基于边缘计算技术的工业主板设计方案也受到越来越多人的关注。…

【Kaggle】Kaggle数据集如何使用命令语句下载?

一、Kaggle数据集如何下载 1.1 问题的起因 最近看到了 Google 组织的 Kaggle 比赛&#xff0c;想自己试一下&#xff0c;但是数据集太大了&#xff0c;将近有370G的数据。直接下载的话&#xff0c;网速太慢&#xff0c;可能要下载3-4天&#xff0c;所以萌生了用命令语句下载的…

LeetCode刷题笔记-287题寻找重复数

LeetCode 287 寻找重复数 难度&#xff1a;中等 题目&#xff1a; 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返回…

fpga开发--蜂鸣器发出连续不同的音调

描述 使用fpga蜂鸣器连续发出do&#xff0c;re&#xff0c;mi&#xff0c;fa&#xff0c;so&#xff0c;la&#xff0c;xi七个不同的音调&#xff0c;每个音调的持续时间为0.5s。 思路 采用状态机实现音调的转化&#xff0c;当do状态持续了0.5s之后转移到re状态&#xff0c;…

c语言位段知识详解

本篇文章带来位段相关知识详细讲解&#xff01; 如果您觉得文章不错&#xff0c;期待你的一键三连哦&#xff0c;你的鼓励是我创作的动力之源&#xff0c;让我们一起加油&#xff0c;一起奔跑&#xff0c;让我们顶峰相见&#xff01;&#xff01;&#xff01; 目录 一.什么是…

【SpringCloud Alibaba】(五)服务雪崩与容错方案

在前面的文章中&#xff0c;我们实现了用户微服务、商品微服务和订单微服务之间的远程调用&#xff0c;并且实现了服务调用的负载均衡。 但是&#xff0c;现在系统中存在着一个很明显的问题&#xff1a;那就是如果系统的并发量上来后&#xff0c;系统并没有容错的能力&#xf…

Onetable:统一的表格式元数据表示

概括 Onehouse 客户现在可以将他们的 Hudi 表查询为 Apache Iceberg 和/或 Delta Lake 表&#xff0c;享受从云上查询引擎到顶级开源项目的原生性能优化。 在数据平台需求层次结构的基础上&#xff0c;存在摄取、存储、管理和转换数据的基本需求。 Onehouse 提供这种基础数据…

C++库封装JNI接口——实现java调用c++

1. JNI原理概述 通常为了更加灵活高效地实现计算逻辑&#xff0c;我们一般使用C/C实现&#xff0c;编译为动态库&#xff0c;并为其设置C接口和C接口。用C实现的一个库其实是一个或多个类的简单编译链接产物。然后暴露其实现类构造方法和纯虚接口类。这样就可以通过多态调用到…

获取各省人口排名数据

目录 一、目标网址 二、网址分析 2.1、获取并解析网页内容 2.2 两种获取方法 三、获取目标数据 四、代码附件 一、目标网址 目标网址&#xff1a;中国人口排名 省份 1.1 网址截图 二、网址分析 2.1、获取并解析网页内容 我们需要使用requests库获取网页&#xff0c;使用…

三个常用查询:根据用户名 / token查询用户信息+链表分页条件查询

目录 1.根据用户名或者token查询用户信息 会员信息实体类 统一状态Result类 controller层 service层及实现类 dao层 测试&#xff1a; 2.链表分页条件查询 会员等级实体类 封装条件类PageVo controller层 service层及实现类 dao层 Mapper.xml层 测试 vue前端参考 1.根据用户名…

多模态大模型(MLLM)之VisCPM:支持中文对话文图双向生成

2020 年 12 月发布的 CPM-1 是国内首个中文大模型 &#xff1b;2022 年 9 月发布的 CPM-Ant 仅微调 0.06% 参数就能超越全参数微调效果&#xff1b;2023 年 5 月发布的 WebCPM 是 中文首个基于搜索的问答开源模型。CPM-Bee 百亿大模型是团队最新发布的基座模型&#xff0c;中文…

基于springboot+mybatis+vue进销存管理信息系统

基于springbootmybatisvue进销存管理信息系统 一、系统介绍二、功能展示1.个人中心2.企业信息管理3.商品信息管理4.客户信息管理5.入库记录管理6.出库记录管理7.出库记录管理8.操作日志管理9.库存盘点管理 四、获取源码 一、系统介绍 系统主要功能&#xff1a; 普通用户&#…

为啥运营商现在严格规范网购流量卡的年龄了呢?20岁以下难了

本期知识点只讲流量卡&#xff0c;葫芦弟今天分享给大家手机流量卡开卡安全的问题。 很多朋友可能觉得电信诈骗离自己很远&#xff0c;但实际上现在已经出现大量收购电话卡用于诈骗的现象&#xff0c;而我们很可能在不知不觉中掉进犯罪分子的陷阱&#xff0c;触犯法律&#xff…