树/二叉树/森林之间的相互转换 与遍历

news2025/8/3 0:11:19

森林就是多棵树的集合,但是森林也可以只有一棵树,二叉树是一种特殊的树,固定的度为2,这是基本前情提要~

树常见的存储方式有三种:

(1)双亲表示法

仅用定义一个结点对象,一个数组,代码定义如下:

typedef struct {
	char data;
	int parent;
} Node;

typedef struct {
	Node node[100];
	int maxSize;
} NodeTree;

在这里插入图片描述
优点:由于对应元素的parent参数存储了父结点在数组中的下标,所以定义某个子结点的父结点非常简单,所以是:找父亲简单
缺点:但是如果想知道某个父亲x有多少个孩子,就必须遍历整个数组,看看哪个元素的parent是x,所以:找儿子们困难

(2)孩子表示法

为了克服找孩子困难问题,那么很容易想到把每个结点的孩子存起来,还是看定义比较简单理解

typedef struct {
	char data;
	int childs[];
} Node;

typedef struct {
	Node node[100];
	int maxSize;
} NodeTree;

在这里插入图片描述
优点:找对应结点的孩子结点们简单
缺点:若要找某个结点的父结点,想象一下是不是应该拿着结点x的下标,去便利数组中所有的结点,看看x的下标在哪个结点的childs数组里面?
(注意,childs数组也可以用链表来实现,都一个意思)

记忆:双亲表示法找双亲简单,找孩子难;孩子表示法找孩子简单,找双亲难!
(3)孩子兄弟表示法

这个存储方式是最常用的,最方便的,而且它能很顺利实现树和二叉树甚至森林的转换,所以它很重要
结构定义上,它就是之前学过的双向链表而已,左指针指向孩子,右指针指向兄弟;也就是背到烂的“左孩子右兄弟”

typedef struct Nodes{
	char data;
	Nodes child;
	Nodes sibling;
} Tree;

在这里插入图片描述
这种数据结构从父亲找孩子,或者从孩子找父亲都会很方便,例如如果需求需要根据孩子再找回父亲,只需要新增一个parent结点指回父亲结点即可(如果有父亲结点的指针,那么某个结点的右子树的parent应该全部一致,因为右兄弟,兄弟们的父亲肯定一样)

typedef struct Nodes{
	char data;
	Nodes parent;
	Nodes child;
	Nodes sibling;
} Tree;

树与二叉树的转换

规则还是“左孩子右兄弟”;不管有多少个兄弟,只要往右子树一直延伸即可,如下图:
在这里插入图片描述

二叉树转换回树

因为根结点没有兄弟,所以它仅有左孩子,所以从二叉树的左结点入手,找左结点的全部右兄弟,使它们连接到左结点的parent结点(下图虚线所示),然后断开兄弟之间的连线(打叉叉的地方),然后再从第一个兄弟的左结点继续往下找,依次
在这里插入图片描述
在这样转还回去的树中,可以发现,G结点放在D的左孩子位置可以,右孩子位置也可以;但是它们都是唯一的一棵树!
当时这个问题我纠结了一下,因为摆放左和右,不是对应不同样的两棵树吗?

后来回头看了资料,说是如果一个结点只有一个孩子,那么不管放在哪个位置,左还是右,都是同一棵树
但是有序树的两个孩子之间的位置就一定不能调换(例如EF的位置不能调换)。 如果是无序树,EF的位置就可以对换,还是同一棵无序树。

森林转换成二叉树

所谓森林,就是多棵树构成的,这非常好理解,
将森林转二叉树,首先运用左孩子右兄弟的规则,把每棵树都单独转成二叉树,然后再运用左孩子右兄弟的规则,把三棵同属于该森林的兄弟二叉树连起来即可,如下图:
在这里插入图片描述

二叉树转换回森林

有了上图的例子,二叉树转回森林其实也非常好理解
(1)首先把根结点的右兄弟单独一个个断开联系,使其成为独立的二叉树
(2)根据二叉树转树的规则,左孩子右兄弟,恢复成原来的树即可
其实就是上图的逆操作过程,这里就不画新图了

遍历

二叉树的遍历:

先序遍历:根左右(NLR)
中序遍历:左根右(LNR)
后序遍历:左右根(LRN)
层次遍历:即从根结点开始,从上往下,从左往右依次扫描结点遍历
(若给出中序和其它任意一种遍历序,都可以还原回唯一的一棵二叉树)

树的遍历

由于树每个结点可能有多个孩子,所以它不可能规定哪个孩子在前的遍历顺序,只能规定父结点优先或者孩子们优先
先根遍历:先访问根结点,再依次遍历孩子结点(若孩子结点又是根结点,则深入下一层继续找孩子的孩子,这种专注于找孩子的遍历方式,很明显用孩子表示法比较简单实现)
后根遍历:从最左下角的孩子开始,先依次遍历各个孩子结点,最后访问根结点,逐层上升
在这里插入图片描述
层次遍历:与二叉树层次遍历一样,从上至下从左到右,依次遍历每个结点

森林的遍历

先序遍历:
先遍历第一棵树的根结点
先序遍历第一棵树中根结点的子树森林
先序遍历第二棵树…
简单点说,就是一棵棵树用先序遍历即可

中序遍历(后序遍历):
记住这里有点坑!所谓森林的中序遍历!其实是先访问第一棵树的子树森林,最后才访问第一棵树的根结点
然后继续开始访问第二棵树的子树森林,最后访问第二棵树的根结点…
以此类推,直到一棵棵树访问完
其实,这就是最后才访问根结点,所以也称之为后序遍历
(森林所生成的对应的二叉树,其森林的中序遍历或者称后序遍历,一定一定一定跟对应的二叉树的中序遍历结果一样!)

这样说很抽象,还是看图:
在这里插入图片描述

例子:

1 : 若T1是有序树T转换而来的二叉树,则T中结点的后根序列就是T1中结点的(中序)序列

因为T1是二叉树,T1是有序树(有序树则说明它的左右结点不能对换位置,是有序的,以此保证二叉树唯一),树的后根遍历,就是二叉树的中序遍历。
解析:
因为树转二叉树后,对应二叉树只有左子树(根结点无右兄弟,所以无右子树),对树做后根遍历,则是先把孩子们扫了一遍,最后扫父亲。而对于仅有左子树的二叉树来说,做中序遍历是:左中右,既然无右子树,其实也只是把左边全部孩子扫一遍,最后扫根结点父亲。那么这两者的顺序就是一样的了。
也可以画图看:
在这里插入图片描述

或者死记:树的后根遍历 = 二叉树的中序遍历
这个在2019年真题中也考到了
(2)设森林F有3棵树,第一第二第三棵树的结点个数分别为M1,M2,M3。与森林F对应的二叉树根结点的右子树上的结点个数是(M2+M3)

这题只需要回想一下上面我们自己推导的森林转成二叉树的过程,不难想象出对应二叉树的右子树其实就是其余两棵树的总结点数

(3)设森林F对应的二叉树为B,它有m个结点,B的根为p,p的右子树结点个数为n,森林F中第一棵树的结点个数是:m-n

森林总结点数为m,即二叉树的总结点数也是m, 根p的右子树结点数有n个,我们根据森林转二叉树规则知道根结点的右子树都是由森林中其它树构成的,既然右子树结点n,也就是除了第一棵树外总的结点数n,所以第一棵树就有m-n个结点

(4)设F是一个森林,B是由F变换来的二叉树,若F中有n个非终端结点,则B中右指针域为空的个数有()个

右指针域为空,也就是没有右兄弟,在森林变成二叉树过程,最后一棵树肯定没有右兄弟,所以它右指针域一定为空
⚠️其次,每一个非终端结点,也就是说肯定会有左孩子,在转换成二叉树后,最后那个孩子肯定没有右兄弟,所以n个非终端结点,会产生n个空的右指针域。
在这里插入图片描述
这种题还是画图,然后看题目中哪个选项符合比较好做。光靠想很难理解…

(5)某二叉树结点的中序序列为BDAECF, 后序序列为DBEFCA,则该二叉树对应的森林包括(3)棵树

首先要根据中序后序复原二叉树,首先抓后序最后一个是根,A
A在中序中的位置,把结点分成左子树BD和右子树ECF
再根据后序“左右中”,确定D肯定是左子树中最先被访问到的左结点,那么B只能是其父结点(B不可能是其右兄弟,因为这样的话他们就要有一个父结点,可是左子树只有他两,D又不父,只能B当父亲)
这样左子树就恢复完成了
右子树中,ECF,根据中序遍历左中右,很明显E是左孩子,C是父结点,F是右孩子
如果不放心,再根据后序看看:EFC,左右中,E还是左孩子,F右孩子,C是父结点
在这里插入图片描述

(6)【2009】将森林转换成对应的二叉树,若在二叉树中,结点U是结点V的父结点的父结点,则在原来森林中,U和V可能具有的关系是:

V和它的父结点若在U的右侧,则在原森林中它们是兄弟关系
V和它的父结点若在U的左侧,则在原森林中V和它的父是U的孩子和孙子关系
若在原森林中U还有父结点,则不管V和它的父结点在U的左侧还是右侧,辈分都是比U的父结点小

(7)【2011】已知一棵有2011个结点的树,其叶子结点个数为116,该树对应的二叉树中无右孩子的结点个数是:

在这里插入图片描述

(8)【2014】将森林F转换为对应的二叉树T,F中叶结点的个数等于(T中左孩子指针为空的结点个数)

因为T为对应二叉树,若T无左孩子,证明对应结点没有孩子(左孩子右兄弟),所以没有左孩子的一定是原森林F中的叶子结点

(6)【2020】已知森林F及与之对应的二叉树T,若F的先根遍历序列是a,b,c,d,e,f ,中根遍历是b,a,d,f,e,c ,则T的后根遍历序列是:
注意森林的中根遍历也是后根遍历,它是一棵棵树逐个遍历的,要注意跟二叉树的后序遍历区分

这个问题只给出了森林F的中根和先根,但是经过上面推导知道,森林的先根和中根遍历与对应的二叉树的先序和中序完全一致,所以直接按照森林的遍历顺序来恢复二叉树
先根的第一个a肯定是根结点
根结点a把中根切成两半,因为中根遍历是“左中右”,那么左边只有一个b, 且b肯定只能是a的左孩子,右边子树部分有d,f,e,c;
再根据先序“中左右”,第三个位置是c,则c是右子树的根结点,C在中根遍历中的位置又会把结点切分成左右子树,可看到dfe都是c的左孩子
再根据先序,第四个位置是d,则d是c的左子树的根结点,也就是c的左孩子,c在中根遍历中的位置再次把结点切分,看到fe是d的右孩子
…这个逻辑慢慢推,就能把二叉树还原出来,最后根据还原的二叉树,做一次后根遍历即可
在这里插入图片描述

(7)【2021】某森林F对应的二叉树为T,若T的先序遍历序列是a,b,d,c,e,g,f 中序遍历是 b,d,a,e,g,c,f 则F中树的棵数是:3

还是按照T的遍历序列先还原,然后看根结点有多少个右孩子即可:
在这里插入图片描述

代码设计:

(1)编程求以孩子兄弟表示法存储的森林的叶子结点数
算法思路:
以孩子兄弟表示法的链式存储结构,则无左孩子的一定是叶子结点,我们仅需定义一个全局变量sum,然后对该森林生成的二叉树进行一次先序遍历,当某个结点没有左孩子,就sum++;直到遍历完成,得到的就是原森林中所有树的所有叶子结点数
伪代码如下:

int sum = 0;

findLeaves(Tree t){
	if(t==null){
		return ;
	}
	if(t.lchild == null){
		sum ++;
	} 
	findLeaves(t.lchild); // 递归找左子树
	findLeaves(t.rchild); // 递归找右子树
}

(2)以孩子兄弟链表为存储结构,设计递归算法求树的深度
算法思路:
求深度,还是可以先序遍历,定义一个全局变量deep和max,每次递归下一层就deep++,判断++后的deep是否大于max最大深度,如果是,把deep赋值给max,记住递归结束的时候要deep–恢复上一层的深度数,不如deep只增不减。
伪代码如下:

int deep = 0;
int max = 0;

findDeep(Tree t, deep){
	if(t==null){
		return 0;
	}
	deep++;
	if(deep > max){
		max = deep;
	}
	findDeep(t.lchild, deep);
	findDeep(t.rchild, deep);
	deep--;
}

(3)已知一棵树的层次序列及每个结点的度,编写算法构造此数的孩子兄弟链表
算法思路:
树转二叉树,左孩子右兄弟,链表存储,第一层只有一个根结点只有左孩子,无右兄弟(右指针为空),第二层结点才可能开始有左孩子和右兄弟(即左右指针不为空)
已知层次序列数组和每个结点的度的数组,首先创建一个根结点T,定义类似双向链表的结构体,把层次序列的第一个元素A塞进根结点T中(根结点无兄弟),然后令它左结点是层次序列的第二个元素B
这个时候从结点度的数组中查找第1个元素A的度(即它会有多少个孩子),把度-1(减去B),for循环拼在B的右指针域链表(A的孩子就是B的兄弟,所以拼在右边);每拼一个,都从层次遍历数组中拿一个元素放入新的结点中,记住这里数组下标要随着变,模拟出队。
再从度的数组中找到B的度,循环给它生成左孩子,链接在左边
在这里插入图片描述
核心是针对层次遍历的数组做每个元素的循环,看看该元素的度多少(在原来树中的度,就代表了它有多少个孩子),还要注意上一层父结点的度数-1的值,下一层第一个孩子就要循环往自己右结点拼多少个兄弟。

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

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

相关文章

windows添加node环境

参考:npm和cnpm(windows)安装步骤_李疆~的博客-CSDN博客_cnpm一、什么是npm和cnpmnpm(node package manager):nodejs的包管理器,用于node插件管理(包括安装、卸载、管理依赖等&#…

YRCloudFile V6.9.0 加速企业在大数据应用技术创新

近日,焱融科技发布分布式文件存储产品 YRCloudFile 6.9.0 版本。该版本在功能和性能方面有多项技术性突破,主要包括大数据应用无缝对接 Hadoop ,为大数据应用提供高性能存储平台;独有的 Dataload 功能和智能分层细粒度化&#xff…

查询不为空的字段

数据&#x1f4ca; idnameparent_id1党组织02陕西党委1314Null25渭南党委Null 实验⁉️ 查询int类型不为空的字段 select * from org where parent_id <> ""❌mybatis 中的 if判断会把整形中的 0 识别为false(空) select * from org where parent_id ! &quo…

体会多态的好

目录 继承和组合&#xff1a; 区别和联系 1.安全性角度&#xff08;封装性&#xff09; 2.灵活性角度&#xff08;牵一发动全身&#xff09; 多态&#xff1a; 一.怎样实现多态 1.完成向上转型 直接赋值&#xff1a;​​​​​​​ 方法传参 方法的返回值 2.完成方法重…

零基础数据科学学习 Python 的 4 个阶段

前言 如果你一直在自学 Python&#xff0c;那么你可能看过无数教程并遵循许多指南来获得这项技能&#xff0c;但是你怎么知道自己走在正确的道路上来掌握这项数据科学的基本技能&#xff1f; &#xff08;文末送读者福利&#xff09; Python 是一种实用的编程语言&#xff0c…

获取CSV文件,转成Excel格式的文件供用户从浏览器下载

前言 最近这十几天每天都很充实&#xff0c;刚完成了几个小需求&#xff1b;今天简单记录一下其中一个&#xff1a;从某系统获取csv文件&#xff0c;然后处理成可供用户在浏览器中下载的Excel文件&#xff1b; 这其中有解除到新的内容&#xff0c;也有利用项目已有的部分功能…

智慧图书馆解决方案-最新全套文件

智慧图书馆解决方案-最新全套文件一、建设背景二、思路架构三、建设方案四、获取 - 智慧图书馆全套最新解决方案合集一、建设背景 现下&#xff0c;传统图书馆已经难以适应时代的发展&#xff0c;图书盘点繁琐、管理模式落后、阅读时间和场地受限等问题&#xff0c;迫使传统图…

148. SAP UI5 表格数据如何导出成 Excel 文件(Table Export As Excel)

本教程前一步骤,我们在介绍 SAP UI5 SmartTable 时,提到了它的 Excel 导出功能。如果将 iseExportToExcel 设置为 true,就可以启用 Excel 导出功能,将 Table 控件显示的数据,导出成本地 Excel 文件。 我们仍然秉承本教程一贯从易到难的学习思路,先暂时不去碰 Smart Table…

30天消化MyBatis源码解析笔记,吊打面试官,offer接到手软

MyBatis 是一个优秀的 Java 持久化框架&#xff0c;SSM 框架组合&#xff08;Spring SpringMVC Mybatis&#xff09;&#xff0c;依赖 MyBatis 搭建的项目更是数不胜数&#xff0c;在互联网公司的使用中&#xff0c;占据了大片江山&#xff0c;你在使用 MyBatis 吗&#xff1…

嵌入式开发:RTOS调试——处理计时问题

毫无疑问&#xff0c;当你开始在嵌入式开发中使用实时操作系统(RTOS)时&#xff0c;会有一个学习曲线。你将在更高的抽象层次上工作&#xff0c;使用或多或少的并行任务&#xff0c;而不仅仅是子例程&#xff0c;并且你需要考虑你的任务应该如何彼此共享数据和处理器时间。你需…

一个系统五个场景,vivo的IoT能否以“慢”取胜?

近年来物联网声浪喧嚣尘上&#xff0c;但其实从移动互联网到IoT只有一步之遥。原因在于手机可以很好的完成“控制中枢”的作用&#xff0c;以手机为核心在IoT领域“开枝散叶”是目前行业的主流选择&#xff0c;这也是小米、华为等手机巨头在IoT领域如鱼得水、产品众多的重要原因…

企业如何防备密码攻击

在身份安全周&#xff0c;了解密码在网络安全中的重要性很有必要&#xff0c;如果您不小心&#xff0c;密码很容易被泄露。ManageEngine ADSelfService Plus如何帮助强化密码并增强企业组织安全性的呢&#xff1f; 现在是 2022 年——密码在今天仍然适用吗&#xff1f; 技术…

Docker(九)—— Docker 网络

我们之前通过端口映射实现过外部主机对容器的访问&#xff0c; 那如果想要实现两个容器之间的通信怎么办&#xff1f;用已有的经验&#xff0c;是像图中蓝色的线那样走吗&#xff1f; 一、Docker0网络 我们运行一个容器&#xff0c;进入容器内部后发现容器也有自己的IP地址。 而…

磁盘占用高问题如何排查?三步教你搞定

作者简介&#xff1a;杨嘉力&#xff0c;OceanBase开源内核高级工程师。 通常情况下&#xff0c;数据库对磁盘的占用量会随着业务的接入时间和业务数据量大增而不断上升&#xff0c;导致磁盘空间不足&#xff0c;进而发生数据无法写入、数据库无法重启等问题。这时我们就需要排…

CENTOS上的网络安全工具(十三)搬到Docker上(1)?

鉴于在集群上构建安全工具的情况越来越频繁&#xff0c;并且现在一些安全工具也提供了Docker形式的部署&#xff0c;再停留在虚拟机yum的部署方式似乎已经不太合时宜了。所以在再一次碰到一个安全工具需要使用docker安装的时候&#xff0c;我们毅然&#xff08;被逼&#xff09…

[附源码]SSM计算机毕业设计中小企业人事管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]Python计算机毕业设计安庆师范大学校园互助平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

C语言学习记录(十二)之字符串和字符串函数

文章目录一、字符串和字符串I/O1.1 定义字符串1.1.1 字符串字面量(字符串常量)1.1.2 字符串数组和初始化1.1.3 数组和指针1.1.4 数组和指针的区别二、字符串输入2.1 分配空间2.2 gets()函数 (不建议使用)2.3 gets()的替代品2.3.1 fgets()函数(和fputs())2.3.2 gets_s()函数2.3.…

【推荐算法毕业设计源码】个性化学习推荐网站的设计及实现丨可定制

登录该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等学习内容。 目录 一、项目介绍&#xff1a; 二、文档学习资料&#xff1a; 三、模块截图&#xff1a; 四、开发技术与运行环境&#xff1a; 五、代码展示&#xff1a; 六、数据库表截图&#x…

UE4 GIS Cesium for Unreal插件的使用 教程

效果&#xff1a;&#xff08;成都郫都区某区域的运行场景&#xff09; 步骤&#xff1a; 1.到虚幻商城搜索 cesiuml&#xff0c;点击Cesium for Unreal 打开后可以看到目前支持的版本有4.26-4.27和5.0 将其安装到引擎 大概0.2G 2.下载完成后&#xff0c;打开4.26版本的虚幻编…