数据结构学习笔记——树的存储结构以及树、森林与二叉树之间的转换

简介: 数据结构学习笔记——树的存储结构以及树、森林与二叉树之间的转换

一、树的存储结构


树的存储结构中反映的是一棵树中各结点之间的关系,在存储中,不仅存储树中每个结点的值,还存储各结点之间的关系,主要有三种存储结构,分别是双亲表示法、孩子链表示法和孩子兄弟表示法。


(一)双亲表示法


双亲表示法是通过采用一维数组来存储树中的结点,其中每个结点被赋予一个结构体类型,包含data域和parent域,分别存储结点的数据域和存储该结点双亲的数组下标。

#define MAXSIZE 100
typedef struct
{
  int data;  //数据域
  int parent;  //双亲位置域
}ParNode[MAXSIZE];


例如,下图该树,通过双亲表示法进行存储,规定从数组下标为0开始存储,根结点下标为0,同时设根结点的parent域为-1:

1667293703606.jpg

1667293712993.jpg


通过双亲表示法中可以很容易地找到每个结点的双亲和祖先,但是其缺点是若寻找结点的孩子结点或兄弟结点就较为麻烦,需遍历整个数组。


(二)孩子链表表示法


将所有结点存放在一个顺序表中,每个数据元素有两个域,分别是该结点的数据域和存放该结点的第一个孩子的地址,同时为树中每个结点都构建一个单链表,它也有两个域,分别是存放该孩子结点在顺序表中的数组下标和存放下一个孩子的地址。

#define MAXSIZE 100
/*顺序表定义*/
typedef struct
{
  int data;    //数据域
  ChildNode *children;  //第一个孩子的地址
}Head;
/*单链表定义*/
typedef struct Node
{
  int address;  //该孩子结点在顺序表中的数组下标
  struct Node *next;  //下一个孩子的地址
}ChildNode;
Head T[MAXSIZE];  //建立顺序表头结构


例如,通过孩子链表表示法进行存储上树,规定顺序表下标为0的位置存放根结点:

1667293738240.jpg


通过孩子链表表示法可以很容易地找到该结点的孩子结点,但是若要找到结点的双亲则较为麻烦,需遍历n个结点中孩子链表指针域所指向的n个孩子链表。


(三)孩子兄弟表示法


孩子兄弟表示法是采用二叉链表,其中包含三个域,分别是数据域和两个指针,即*child指向第一个孩子结点的地址、*brother指向该结点的下一个兄弟结点的地址。

typedef struct Node
{
  int data;  //数据域
  struct Node *child,*brother;  //第一个孩子结点的地址和下一个兄弟结点的地址
}ChildBNode;


例如,通过孩子兄弟表示法进行存储上树:

1667293757948.jpg


这种方法的缺点是若从当前结点查找其双亲结点较为麻烦,解决方法是为每个结点增设一个parent域用于指向其父结点。


二、树、森林与二叉树的转换


(一)树转换成二叉树


例如,下图是一棵树,将其转换成二叉树:

1667293782438.jpg

1、首先为树中所有兄弟结点之间连线:

1667293791113.jpg

2、对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除:

1667293800469.jpg

3、以根结点为轴心,顺时针旋转45°,即可得到下图二叉树:

1667293809326.jpg

我们可以得出,一棵树转换为二叉树的途中,转换之后的右孩子结点是其原本的兄弟,例如结点C、D原本是兄弟,然后变成了B、C的右孩子,转换之后的二叉树的根结点无右子树。


(二)二叉树还原成树


二叉树还原成树的前提是该二叉树的根结点无右子树,将上图二叉树还原成树的步骤如下:

1、若某结点的左孩子有右子树,则在该结点与其左孩子的右子树的右分支上结点连线,例如结点A的左孩子B有右子树,则在结点A和其左孩子的右子树的右分支上连线:

1667293821611.jpg

2、删除各结点右子树的右分支与其父结点连线(图中橙线):

1667293830143.jpg

删除后:

1667293838859.jpg

3、旋转整理即可还原得到树:

1667293845630.jpg


(三)森林转换成二叉树


森林转换成二叉树的大体步骤是采用孩子兄弟表示法,森林中的第一棵树的根结点为二叉树的根结点,第一棵树的子树为二叉树的左子树,二叉树的右子树为森林中剩余的树。

例如,下图是森林,将其转换成二叉树:

1667293857044.jpg

1、将该森林中每棵树转换为二叉树,为各个树中所有兄弟结点之间连线,即此时结点的左指针指向该结点的孩子,右指针指向该结点的兄弟结点:

1667293865053.jpg

2、将每棵树的根结点都视为兄弟结点的关系,将其连线:

1667293872400.jpg

3、然后对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除其余连线都删除:

1667293879752.jpg

4、以第一棵树的根结点为轴心,顺时针旋转45°,整理后,即可得到下图二叉树:

1667293886693.jpg


(四)二叉树还原成森林


将上图二叉树还原成森林的步骤如下:

1、若某结点是其双亲的左孩子,则将该结点的右孩子、右孩子的右孩子等等都与该结点的双亲结点连接:

1667293896788.jpg

2、删除二叉树中所有双亲结点与其右孩子结点的连线:

1667293904454.jpg

3、整理所得到的树和森林:

1667293912890.jpg


✨一个高度为h(h>0)的满二叉树对应的森林中所含的树的棵数为h。

注:不能说成是一颗完全二叉树,满二叉树是完全二叉树的特例,因为若一棵树是满二叉树,则它必是完全二叉树,但不能说一个完全二叉树必是满二叉树。


例如,下图是一棵高度为3的满二叉树,将其转换为森林:

1667293921552.jpg

1667293929785.jpg

1667293937232.jpg

由于该满二叉树的高度为3,即将其转换为森林后,该森林所含有的树也为3:

1667293950131.jpg


这个性质如果对于完全二叉树,则不成立。


例如,下图是一棵高度为3的完全二叉树,将其转换为森林:

1667293959257.jpg

1667293967195.jpg

1667293974128.jpg


可发现,经整理后,只得到两棵树,与高度无关:

1667293984792.jpg


三、树和森林的遍历


(一)树的遍历


树的遍历主要分为两种:先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该树:

1667293994981.jpg

该树的先序遍历序列为ABDEHMI,后序遍历序列为DBHMEIA。

另外也有层次遍历,该树的层次遍历序列为ABEIDHM。

将上图中的树转为二叉树后,如下:

1667294010029.jpg

我们知道该二叉树的先序遍历序列为ABDEHMI,中序遍历序列为DBHMEIA,后序遍历序列为DMHIEBA,可以得出:


✨树的先序遍历序列为转换成二叉树后的先序遍历序列,树的后序遍历序列为转换成二叉树后的中序遍历序列。


(二)森林的遍历


森林的遍历也是主要分为两种:

先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该森林:

1667294028755.jpg

对其进行先序遍历和后序遍历得到的序列分别为ABDEHMICFJKGL、DBHMEIAJFKCLG。

将上图中的森林转为二叉树后,如下:

1667294036951.jpg

对该二叉树进行先序遍历、中序遍历和后序遍历得到的遍历序列为:ABDEHMICFJKGL、DBHMEIAJFKCLG、DMHIEBJKFLGCA。

不难看出,可以得到以下结论:


✨森林的先序遍历序列为转换成二叉树后的先序遍历序列,森林的后序遍历序列为转换成二叉树后的中序遍历序列。

总结:

对应关系 森林 二叉树
- 先序遍历 先序遍历 先序遍历
- 后序遍历 中序遍历 中序遍历


只需记住:

1、树、森林、二叉树的先序遍历序列都是相同的。

2、森林和二叉树的先序遍历和中序遍历序列是相同的。


相关文章
|
8天前
|
JSON 前端开发 JavaScript
一文了解树在前端中的应用,掌握数据结构中树的生命线
该文章详细介绍了树这一数据结构在前端开发中的应用,包括树的基本概念、遍历方法(如深度优先遍历、广度优先遍历)以及二叉树的先序、中序、后序遍历,并通过实例代码展示了如何在JavaScript中实现这些遍历算法。此外,文章还探讨了树结构在处理JSON数据时的应用场景。
一文了解树在前端中的应用,掌握数据结构中树的生命线
|
24天前
|
C语言
数据结构基础详解(C语言):图的基本概念_无向图_有向图_子图_生成树_生成森林_完全图
本文介绍了图的基本概念,包括图的定义、无向图与有向图、简单图与多重图等,并解释了顶点度、路径、连通性等相关术语。此外还讨论了子图、生成树、带权图及几种特殊形态的图,如完全图和树等。通过这些概念,读者可以更好地理解图论的基础知识。
|
26天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
5天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
01_设计一个有getMin功能的栈
01_设计一个有getMin功能的栈
|
5天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
06_用栈来求解汉诺塔问题
06_用栈来求解汉诺塔问题
05_用一个栈实现另一个栈的排序
05_用一个栈实现另一个栈的排序
03_如何仅用递归函数和栈操作逆序一个栈
03_如何仅用递归函数和栈操作逆序一个栈
|
5天前
|
测试技术
02_由两个栈组成的队列
02_由两个栈组成的队列