一、树的存储结构
树的存储结构中反映的是一棵树中各结点之间的关系,在存储中,不仅存储树中每个结点的值,还存储各结点之间的关系,主要有三种存储结构,分别是双亲表示法、孩子链表示法和孩子兄弟表示法。
(一)双亲表示法
双亲表示法是通过采用一维数组来存储树中的结点,其中每个结点被赋予一个结构体类型,包含data域和parent域,分别存储结点的数据域和存储该结点双亲的数组下标。
#define MAXSIZE 100 typedef struct { int data; //数据域 int parent; //双亲位置域 }ParNode[MAXSIZE];
例如,下图该树,通过双亲表示法进行存储,规定从数组下标为0开始存储,根结点下标为0,同时设根结点的parent域为-1:
通过双亲表示法中可以很容易地找到每个结点的双亲和祖先,但是其缺点是若寻找结点的孩子结点或兄弟结点就较为麻烦,需遍历整个数组。
(二)孩子链表表示法
将所有结点存放在一个顺序表中,每个数据元素有两个域,分别是该结点的数据域和存放该结点的第一个孩子的地址,同时为树中每个结点都构建一个单链表,它也有两个域,分别是存放该孩子结点在顺序表中的数组下标和存放下一个孩子的地址。
#define MAXSIZE 100 /*顺序表定义*/ typedef struct { int data; //数据域 ChildNode *children; //第一个孩子的地址 }Head; /*单链表定义*/ typedef struct Node { int address; //该孩子结点在顺序表中的数组下标 struct Node *next; //下一个孩子的地址 }ChildNode; Head T[MAXSIZE]; //建立顺序表头结构
例如,通过孩子链表表示法进行存储上树,规定顺序表下标为0的位置存放根结点:
通过孩子链表表示法可以很容易地找到该结点的孩子结点,但是若要找到结点的双亲则较为麻烦,需遍历n个结点中孩子链表指针域所指向的n个孩子链表。
(三)孩子兄弟表示法
孩子兄弟表示法是采用二叉链表,其中包含三个域,分别是数据域和两个指针,即*child指向第一个孩子结点的地址、*brother指向该结点的下一个兄弟结点的地址。
typedef struct Node { int data; //数据域 struct Node *child,*brother; //第一个孩子结点的地址和下一个兄弟结点的地址 }ChildBNode;
例如,通过孩子兄弟表示法进行存储上树:
这种方法的缺点是若从当前结点查找其双亲结点较为麻烦,解决方法是为每个结点增设一个parent域用于指向其父结点。
二、树、森林与二叉树的转换
(一)树转换成二叉树
例如,下图是一棵树,将其转换成二叉树:
1、首先为树中所有兄弟结点之间连线:
2、对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除:
3、以根结点为轴心,顺时针旋转45°,即可得到下图二叉树:
我们可以得出,一棵树转换为二叉树的途中,转换之后的右孩子结点是其原本的兄弟,例如结点C、D原本是兄弟,然后变成了B、C的右孩子,转换之后的二叉树的根结点无右子树。
(二)二叉树还原成树
二叉树还原成树的前提是该二叉树的根结点无右子树,将上图二叉树还原成树的步骤如下:
1、若某结点的左孩子有右子树,则在该结点与其左孩子的右子树的右分支上结点连线,例如结点A的左孩子B有右子树,则在结点A和其左孩子的右子树的右分支上连线:
2、删除各结点右子树的右分支与其父结点连线(图中橙线):
删除后:
3、旋转整理即可还原得到树:
(三)森林转换成二叉树
森林转换成二叉树的大体步骤是采用孩子兄弟表示法,森林中的第一棵树的根结点为二叉树的根结点,第一棵树的子树为二叉树的左子树,二叉树的右子树为森林中剩余的树。
例如,下图是森林,将其转换成二叉树:
1、将该森林中每棵树转换为二叉树,为各个树中所有兄弟结点之间连线,即此时结点的左指针指向该结点的孩子,右指针指向该结点的兄弟结点:
2、将每棵树的根结点都视为兄弟结点的关系,将其连线:
3、然后对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除其余连线都删除:
4、以第一棵树的根结点为轴心,顺时针旋转45°,整理后,即可得到下图二叉树:
(四)二叉树还原成森林
将上图二叉树还原成森林的步骤如下:
1、若某结点是其双亲的左孩子,则将该结点的右孩子、右孩子的右孩子等等都与该结点的双亲结点连接:
2、删除二叉树中所有双亲结点与其右孩子结点的连线:
3、整理所得到的树和森林:
✨一个高度为h(h>0)的满二叉树对应的森林中所含的树的棵数为h。
注:不能说成是一颗完全二叉树,满二叉树是完全二叉树的特例,因为若一棵树是满二叉树,则它必是完全二叉树,但不能说一个完全二叉树必是满二叉树。
例如,下图是一棵高度为3的满二叉树,将其转换为森林:
由于该满二叉树的高度为3,即将其转换为森林后,该森林所含有的树也为3:
这个性质如果对于完全二叉树,则不成立。
例如,下图是一棵高度为3的完全二叉树,将其转换为森林:
可发现,经整理后,只得到两棵树,与高度无关:
三、树和森林的遍历
(一)树的遍历
树的遍历主要分为两种:先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该树:
该树的先序遍历序列为ABDEHMI,后序遍历序列为DBHMEIA。
另外也有层次遍历,该树的层次遍历序列为ABEIDHM。
将上图中的树转为二叉树后,如下:
我们知道该二叉树的先序遍历序列为ABDEHMI,中序遍历序列为DBHMEIA,后序遍历序列为DMHIEBA,可以得出:
✨树的先序遍历序列为转换成二叉树后的先序遍历序列,树的后序遍历序列为转换成二叉树后的中序遍历序列。
(二)森林的遍历
森林的遍历也是主要分为两种:
先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该森林:
对其进行先序遍历和后序遍历得到的序列分别为ABDEHMICFJKGL、DBHMEIAJFKCLG。
将上图中的森林转为二叉树后,如下:
对该二叉树进行先序遍历、中序遍历和后序遍历得到的遍历序列为:ABDEHMICFJKGL、DBHMEIAJFKCLG、DMHIEBJKFLGCA。
不难看出,可以得到以下结论:
✨森林的先序遍历序列为转换成二叉树后的先序遍历序列,森林的后序遍历序列为转换成二叉树后的中序遍历序列。
总结:
对应关系 | 树 | 森林 | 二叉树 |
- | 先序遍历 | 先序遍历 | 先序遍历 |
- | 后序遍历 | 中序遍历 | 中序遍历 |
只需记住:
1、树、森林、二叉树的先序遍历序列都是相同的。
2、森林和二叉树的先序遍历和中序遍历序列是相同的。