数据结构6.4_树和森林

数据结构教程:http://data.biancheng.net/view/30.html

一、树的存储结构

在使用树结构描述实际问题时,大多数不是二叉树,更多的是普通的树结构,在存储具有普通树结构的数据时,经常使用的方法有3种:

1、双亲表示法

 取一块连续的内存空间,在存储每个结点的同时,各自都附加一个记录其父结点位置的变量。

双亲表存储表示:

 1 #define tree_size 100//宏定义树中结点的最大数量
 2 #define TElemType int//宏定义树结构中数据类型
 3 typedef struct PTNode{
 4     TElemType data;//树中结点的数据类型
 5     int parent;//结点的父结点在数组中的位置下标
 6 }PTNode;
 7 typedef struct {
 8     PTNode nodes[tree_size];//存放树中所有结点
 9     int r,n;//根的位置下标和结点数
10 }PTree;

                                双亲表示法

当算法中需要在树结构中频繁地查找某结点的父结点时,使用双亲表示法最合适。当频繁地访问结点的孩子结点时,双亲表示法就很麻烦,采用孩子表示法就很简单。

2、孩子表示法

将树中的每个结点的孩子结点排列成一个线性表,用链表存储起来。对于含有 n 个结点的树来说,就会有 n 个单链表,将 n 个单链表的头指针存储在一个线性表中,这样的表示方法就是孩子表示法。

如果结点没有孩子(例如叶子结点),那么它的单链表为空表。

#define TElemType int
#define Tree_Size 100
//孩子表示法
typedef struct CTNode{
    int child;//链表中每个结点存储的不是数据本身,而是数据在数组中存储的位置下标
    struct CTNode * next;
}*ChildPtr;
typedef struct {
    TElemType data;//结点的数据类型
    ChildPtr firstchild;//孩子链表的头指针
}CTBox;
typedef struct{
    CTBox nodes[Tree_Size];//存储结点的数组
    int n,r;//结点数量和树根的位置
}CTree;

 

                               孩子表示法

使用孩子表示法存储的树结构,正好和双亲表示法相反,适用于查找某结点的孩子结点,不适用于查找其父结点。可以将两种表示方法合二为一,存储效果如下图: 

                                    孩子双亲表示法

3、孩子兄弟表示法

使用链式存储结构存储普通树。链表中每个结点由 3 部分组成:

其中孩子指针域,表示指向当前结点的第一个孩子结点,兄弟结点表示指向当前结点的下一个兄弟结点。

1 #define ElemType int
2 typedef struct CSNode{
3     ElemType data;
4     struct CSNode * firstchild,*nextsibling;
5 }CSNode,*CSTree;

通过孩子兄弟表示法,普通树转化为了二叉树,所以孩子兄弟表示法又被称为“二叉树表示法”或者“二叉链表表示法”。

=====================================================

二、森林与二叉树的转换

二叉树和树都可用二叉链表作为存储结构,则二叉链表作为媒介可导出树与二叉树之间的一个对应关系。

也就是说,给定一棵树,可以找到唯一的一棵二叉树与之对应。

从物理结构上看,它们的二叉链表是相同的,只是解释不同而已。

 而森林是由多棵树组成,为了便于对森林的遍历等操作,需要将森林中的所有树都组合成一颗大的二叉树,转化步骤为:

    1. 首先将森林中树各自转化为二叉树;
    2. 森林中第一棵二叉树的树根作为转化后二叉树的树根;
    3. 其他树的树根作为第一棵树树根的兄弟结点,进行连接;

 

如图所示,(A)中由三棵普通树组成的森林,首先三棵普通树采用孩子兄弟表示法各自转化成二叉树,如(B)所示;

然后由(B)转(C)时,将森林中第一棵树的树根作为转化后的整棵二叉树的树根,其他数的树根作为第一棵树的树根的兄弟结点,如(C)所示。



=====================================================

三、树和森林的遍历 

转化成二叉树的森林,做的最多的操作就是查找树中的结点。在遍历转化后的二叉树时,遍历方式有先序遍历、中序遍历和后序遍历。

 

对森林使用先序和中序遍历的结果和对转化后的二叉树使用先序和中序遍历得到的序列是一样的,而使用后序遍历得到的结果不同。

例如图 (B)中森林采用中序遍历和(C)中二叉树采用中序遍历得到的结果是相同的,遍历序列都为:B C D A F E H J I G

由树结构的定义可引出两种次序遍历树的方法:

一种是先根(次序)遍历树,先访问树的根结点,然后依次先根遍历根的每棵子树;

另一种是后根(次序)遍历树,即:先依次后根遍历每棵子树,然后访问根结点;

对下图的树先进行先根遍历,可得到树的先根序列为:

A B C D E

若对此树进行后根遍历,则得树的后根序列为:

B D C E A

按照森林和树相互递归的定义,我们可以推出森林的两种遍历方法:

1、先序遍历森林

若森林非空,则可按下述规则遍历之

1)访问森林中第一棵树的根结点;

2)先序遍历第一个棵树种根结点的子树森林;

3)先序遍历除去第一棵树之后剩余的树构成的森林;

2、中序遍历森林

若森林非空,则可按下述规则遍历之:

1)中序遍历森林中第一棵树的根结点的子树森林;

2)访问第一棵树的根结点;

3)中序遍历除去第一棵树之后剩余的树构成的森林。

对上图森林进行先序遍历和中序遍历,则分别得到森林的先序序列:

A B C D E F G H I J

中序序列:

B C D A F E H I J G

当二叉链表作为树的存储结构时,树的先根遍历和后根遍历可借用二叉树的先序遍历和中序遍历算法实现之。

原文地址:https://www.cnblogs.com/grooovvve/p/10822478.html