vlambda博客
学习文章列表

树的存储结构及二叉树与树的转换

双亲表示法(顺序存储)

每个结点中保存指向双亲的“指针”。(类似于静态链表)

根结点存储在数组0号位置,-1表示没有双亲。

 #define MAX_TREE_SIZE 100 //树中最多结点数 //树的结点定义 typedef struct{  ElemType data; int parent; }PTNode;
//树的类型定义 typedef struct{ PTNode nodes[MAX_TREE_SIZE]; int n; }PTree;

增加

双亲表示法增加一个结点很容易,只需在数组后加入对应的data,parent。增加的结点不必按逻辑上的次序存储。

删除

删除一个结点分两种情况:

  • 如果删除的结点是叶子结点

    • 把结点的parent置为-1。

    • 把尾部结点数据移到要删除的结点位置。这样可以保证数组中的结点数据都是有效的。

      注意:删除一个结点后,还需要更改结点数n。

  • 如果删除的结点不是叶子结点。则还要把该结点的子孙结点都删除。

    因此就涉及到查询操作,即从一个结点找到它的孩子结点。使用双亲表示法的存储结构利用了每个结点(除根结点外)都有唯一双亲的性质,可以很快得到每个结点的双亲结点,但求结点的孩子时需要从数组头部开始遍历整个数组。

    (从而可以看出,如果使用直接将parent置为-1的方法,则会导致在遍历数组时多次无效的开销。因此推荐使用第二种删除方法)


孩子表示法(顺序存储+链式存储)

这种存储方式寻找子女的操作非常直接,而寻找双亲的操作需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。

树的存储结构及二叉树与树的转换

 #define MAX_TREE_SIZE 100 struct CTNode{ int child; //孩子结点在数组中的位置 struct CTNode *next; //下一个孩子 };
typedef struct{ ElemType data; struct CTNode *firstChild; //第一个孩子 } CTBox;
typedef struct{ CTBox nodes[MAX_TREE_SIZE]; int n,r; //结点数和根的位置 } CTree;

孩子兄弟表示法(链式存储)

孩子兄弟表示法又称二叉树表示法,即以二叉链表作为树的存储结构。

每个结点包含三部分内容:结点值、指向结点第一个孩子结点的指针,及指向结点下一个兄弟结点的指针(沿次指针域可以找到结点的所有兄弟结点)。

将树转换为二叉树的链式存储。

树的存储结构及二叉树与树的转换



树与二叉树的转换

其实就是利用树的孩子兄弟表示法的规则,将树转换为二叉链表。

树中结点的第一个孩子,相当于二叉树中结点的左孩子。

树中结点的兄弟结点,相当于二叉树中结点的右孩子。

二叉树转树

二叉树的叶子结点和树的叶子结点数并不一定相同。

树的存储结构及二叉树与树的转换

森林和二叉树的转换

森林转换成二叉树

本质也是可以用二叉链表存储森林。先将每个树转换为二叉树,然后将每个树的根结点看成兄弟结点,依次连接各个二叉树。就可以得到森林对应的二叉树。

二叉树转换成森林


总结:树、森林和其对应二叉树的某些性质之间的联系

遍历树/森林对应的二叉树时,往左是下级,往右是平级。简称:左孩子,右兄弟。

叶子结点和非叶子结点的关系:

②森林/树的叶子结点的个数 =  森林/树的总结点个数  -  森林/树的非叶子结点个数

③森林/树的总结点个数 = 对应二叉树的总结点个数

由上可以推出一系列的推论:

森林中非叶子结点个数 + 1 = 对应二叉树中右指针域为空的结点个数

注:树或森林中非叶子结点说明一定有孩子,那么它的最后一个孩子在对应二叉树中右指针域一定为空,再加上最后一个

森林中叶子结点个数 =  对应二叉树中左指针域为空的结点个数

注:森林或树中叶子结点没有孩子,对应于二叉树中左指针域为空

度的关系

树的度数 = 其对应二叉树的度数

森林的度数 = 总结点数 - 森林中树的个数 =  对应二叉树的度数 + 1 - 森林中树的个数

高度/深度的关系:

森林中树的个数等于二叉树中从根结点往右走的结点数,所以不一定等于二叉树的高度。如果二叉树是满二叉树,则高度等于对应森林中树的个数。

树和森林的高度都不一定 =  对应二叉树的高度。