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


    一、树的存储结构

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

    (一)双亲表示法

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

    #define MAXSIZE 100
    typedef struct
    {
    	int data;		//数据域
    	int parent;		//双亲位置域
    }ParNode[MAXSIZE];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    例如,下图该树,通过双亲表示法进行存储,规定从数组下标为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];		//建立顺序表头结构
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    例如,通过孩子链表表示法进行存储上树,规定顺序表下标为0的位置存放根结点:
    在这里插入图片描述

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

    (三)孩子兄弟表示法

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

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

    例如,通过孩子兄弟表示法进行存储上树:
    在这里插入图片描述

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

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

    (一)树转换成二叉树

    例如,下图是一棵树,将其转换成二叉树:
    在这里插入图片描述
    1、首先为树中所有兄弟结点之间连线:
    在这里插入图片描述
    2、对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除:
    在这里插入图片描述
    3、以根结点为轴心,顺时针旋转45°,即可得到下图二叉树:
    在这里插入图片描述
    我们可以得出,一棵树转换为二叉树的途中,转换之后的右孩子结点是其原本的兄弟,例如结点C、D原本是兄弟,然后变成了B、C的右孩子,转换之后的二叉树的根结点无右子树。

    (二)二叉树还原成树

    二叉树还原成树的前提是该二叉树的根结点无右子树,将上图二叉树还原成树的步骤如下:
    1、若某结点的左孩子有右子树,则在该结点与其左孩子的右子树的右分支上结点连线,例如结点A的左孩子B有右子树,则在结点A和其左孩子的右子树的右分支上连线:
    在这里插入图片描述
    2、删除各结点右子树的右分支与其父结点连线(图中橙线):
    在这里插入图片描述
    删除后:
    在这里插入图片描述
    3、旋转整理即可还原得到树:
    在这里插入图片描述

    (三)森林转换成二叉树

    例如,下图是一棵森林,将其转换成二叉树:
    在这里插入图片描述
    1、将该森林中每棵树转换为二叉树,为各个树中所有兄弟结点之间连线:
    在这里插入图片描述
    2、每棵树的根结点也可以视为兄弟,将其连线:
    在这里插入图片描述
    3、然后对树中每个结点只保留它与左边第一个孩子结点之间的连线,其余连线都删除其余连线都删除:
    在这里插入图片描述
    4、以第一棵树的根结点为轴心,顺时针旋转45°,整理后,即可得到下图二叉树:
    在这里插入图片描述

    (四)二叉树还原成森林

    将上图二叉树还原成森林的步骤如下:
    1、若某结点是其双亲的左孩子,则将该结点的右孩子、右孩子的右孩子等等都与该结点的双亲结点连接:
    在这里插入图片描述
    2、删除二叉树中所有双亲结点与其右孩子结点的连线:
    在这里插入图片描述
    3、整理所得到的树和森林:
    在这里插入图片描述

    三、树和森林的遍历

    (一)树的遍历

    树的遍历主要分为两种:先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该树:
    在这里插入图片描述
    该树的先序遍历序列为ABDEHMI,后序遍历序列为DBHMEIA
    另外也有层次遍历,该树的层次遍历序列为ABEIDHM
    将上图中的树转为二叉树后,如下:
    在这里插入图片描述
    我们知道该二叉树的先序遍历序列为ABDEHMI,中序遍历序列为DBHMEIA,后序遍历序列为DMHIEBA,可以得出:

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

    (二)森林的遍历

    森林的遍历也是主要分为两种:
    先序遍历和后序遍历,和二叉树的遍历规则一样,例如下图该森林:
    在这里插入图片描述
    对其进行先序遍历和后序遍历得到的序列分别为ABDEHMICFJKGLDBHMEIAJFKCLG
    将上图中的森林转为二叉树后,如下:
    在这里插入图片描述
    对该二叉树进行先序遍历、中序遍历和后序遍历得到的遍历序列为:ABDEHMICFJKGLDBHMEIAJFKCLGDMHIEBJKFLGCA
    不难看出,可以得到以下结论:

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

    总结:

    对应关系森林二叉树
    -先序遍历先序遍历先序遍历
    -后序遍历中序遍历中序遍历
  • 相关阅读:
    1855. 下标对中的最大距离
    武汉星起航跨境—跨境电商卖家向海外仓进发,提升物流时效
    特征工程特征预处理归一化与标准化、鸢尾花种类预测代码实现
    学习笔记13--路径-速度分解法之汽车速度规划
    Linux入门教程:P11->文件查找类
    TOGAF(企业架构)
    浏览器、负载均衡 、进程内部层…那些你需要掌握的多级缓存
    正则表达式:字符(2)
    vue3 axios封装
    ARC123E Training
  • 原文地址:https://blog.csdn.net/qq_43085848/article/details/126111622