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

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

一、树的存储结构


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


(一)双亲表示法


双亲表示法是通过采用一维数组来存储树中的结点,其中每个结点被赋予一个结构体类型,包含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、森林和二叉树的先序遍历和中序遍历序列是相同的。


相关文章
|
14天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
57 16
|
14天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
63 8
|
1月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
19 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
1月前
|
Java C++
【数据结构】探索红黑树的奥秘:自平衡原理图解及与二叉查找树的比较
本文深入解析红黑树的自平衡原理,介绍其五大原则,并通过图解和代码示例展示其内部机制。同时,对比红黑树与二叉查找树的性能差异,帮助读者更好地理解这两种数据结构的特点和应用场景。
28 0
|
1月前
|
存储 算法
探索数据结构:分支的世界之二叉树与堆
探索数据结构:分支的世界之二叉树与堆
|
15天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
6天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
11天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
13天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
41 4

热门文章

最新文章