数据结构(十九) -- C语言版 -- 树 - 树、森林、二叉树的江湖爱恨情仇、相互转换

零、读前说明

  • 本文中所有设计的代码均通过测试,并且在功能性方面均实现应有的功能。
  • 设计的代码并非全部公开,部分无关紧要代码并没有贴出来。
  • 如果你也对此感兴趣、也想测试源码的话,可以私聊我,非常欢迎一起探讨学习。
  • 由于时间、水平、精力有限,文中难免会出现不准确、甚至错误的地方,也很欢迎大佬看见的话批评指正。
  • 嘻嘻。。。。 。。。。。。。。收!
摘要
  

  在满足树的条件下,树可以是任意形状,一个节点可以有任意多个孩子。在前面的存储结构中,提到了树的孩子兄弟方法可以将一个树用二叉链表进行存储,所以借助二叉链表,树和二叉树可以进行相互转换,从物理结构来看,他们的二叉链表是相同的。因此,只要我们设定一定的规则,用二叉树来表示树甚至森林是可以,森林与二叉树也可以相互转换。(摘自《大话数据结构》 P196

  关于树、森林、二叉树等基础知识等,可以查看博文:数据结构(十) – C语言版 – 树 - 基础知识。

一、树转换为二叉树

  在前面的博文 数据结构(十) – C语言版 – 树 - 基础知识 里面已经说过了树的 左孩子-右兄弟表示法,他就可以将一个转化为一个类二叉树(没有区分左右孩子节点)。具体转换的方法可以总结为:

  1、加入连接线 :在树中所有的兄弟节点(同一双亲下的同一层的节点(孩子之间互称兄弟))之间加入一条连接线
  2、删除连接线 :对树中每一个节点,只保留他与最左边第一个孩子节点的连接点,删掉他与其他孩子节点的连接线
  3、调整层次顺序:以树的根节点为根节点,依次按照连接线的形式将节点调整到层次分明,左右清楚

  注意第一个孩子节点是二叉树根节点的左孩子,兄弟节点转换过来的孩子节点是右孩子

  举个栗子:用下图中表示的树进行举例说明,其中因为是一个简单的树的结构,在一个节点下面可能会有任意多个节点,所以也就是没有左右节点的说法了, 这里我们使用左右的顺序关系,从左依次到右来表示。
  
在这里插入图片描述

图1.1 树的简单表示形式

  
  从上面的图片在中,我们可以看到,节点 B、C、D 为一组兄弟节点,节点 E、F 为一组兄弟节点、节点 H、I、J 为一组兄弟节点,那么首先将各个兄弟节点连接起来,下图左侧表示( 红色 线条)。

  然后将除了长子(最左边节点)节点之间的连接线删除(下图中右侧用 灰色虚线 表示)。

在这里插入图片描述

图1.2 兄弟加线、删线的图例

  
  然后进行节点层次的调整,图中节点 B 为节点 A长子,节点 E 为节点 B长子,节点 H 为节点 D长子,那么在调整时,将长子调整为节点的左孩子,然后将兄弟节点调整为其右孩子。所以,节点 B、E、H 分别为其双亲节点的左孩子其他兄弟节点为右孩子。并且节点 H、I、J兄弟节点,在调整过程中,将节点 I 调整为节点 H右孩子,节点 GI右孩子,调整效果如下图所示。
  
在这里插入图片描述

图1.3 层次调整示意图

  

二、二叉树转换为树

  这个相对比较简单了,其实就是上面的一个逆过程,但是实际的操作过程还是先加线、再删线、后调整次序的过程,只是操作的对象略有不同。那么其转换的过程可以描述为:

  1、连接节点:如果节点存在左孩子,那么将这个左孩子的右孩子节点、右孩子的右孩子节点,右孩子的右孩子的右孩子节点…都和此节点连接起来
  2、删除连接:删除原本二叉树中的所有节点与其右孩子节点的连接线
  3、调整层次:依次按照连接线的形式将节点调整到层次分明,左右清楚

  过程可以用下面的图片来一次表示。

  首先如下图左边的二叉树为例,我们分别将右孩子、右孩子的右孩子…都与爷爷节点连接起来(下图中右边图例中 红色 线条表示)。

在这里插入图片描述

图2.1 节点连接示意图

  然后将二叉树中所有节点和其右孩子的连接线删除,如下图所示(灰色虚线表示)。

在这里插入图片描述

图2.2 删除节点连接示意图

  
  最后调整完层次后,可以得到和上图1.1中一模一样的树。详情 点我可以查看图1.1

  但是有一个问题:上面的二叉树的根节点是没有右孩子的,所有可以将二叉树转换成树。那么如果根节点存在右孩子,那么应该怎么转换呢?看看下面。

三、二叉树转换为森林

  首先来看看什么是森林?

  森林:指的是多个不相交的树的集合

  根据前面的标识。可以将一个二叉树转换为树,那么应该怎么转换为森林呢?

  判断一个二叉树能够转换成树还是森林,需要判断根节点是否存在右孩子,存在右孩子就是森林,不存在右孩子就是树。

  所以,将一个二叉树转换为森林就可以总结为:

  1、从根节点开始,如果存在右孩子,则将其与右孩子的连接线删除,然后在判断分离后的二叉树,如果存在右孩子则删除连接线…直到所有的与右孩子的连接线删除为止。

  2、将每一个分离出来的二叉树转换为树即可。具体可以 点我可以查看详细步骤

  过程可以用下面的图片来一次表示。

  首先将右孩子的连接线断开(下图中虚线表示)。

在这里插入图片描述

图3.1 断开右孩子连线

  
  然后将二叉树转换为树即可,如下图表示。
  
在这里插入图片描述

图3.1 断开右孩子连线

  

四、森林转换为二叉树

  在森林中,有若干个独立的树组成,下图所示。
  
在这里插入图片描述

图4.1 层次调整示意图

  
  那么将一个森林转换成一个二叉树,那么可以现需要将每个独立的树先转换成二叉树,然后将多个二叉树在组合成二叉树接口。所以,具体的方法可以描述为:

  1、将树中每一个树转换成二叉树,转换方式与前面的一致。
  2、第一个二叉树不动,从第二个二叉树开始,依次把后面的一个二叉树的根节点作为前一个二叉树的根节点的右孩子链接起来(也就是将各个树的根节点依次从左到右链接)。直到所有二叉树链接完成。

  所以,具体的步骤可以用下面来表示。

  首先将每一个树转换成二叉树,还是使用左孩子有兄弟的方式来转换,转换完成如下图所示。

图4.2 每个树转换为二叉树

  然后依次拼接二叉树,作为根节点的右孩子,其实也就是将各个树的根节点从左到右依次链接起来,然后稍微调整层次即可完成,所以如下图所示。

图4.3 依次链接各个树

  

五、树与森林的遍历

  树和森林的遍历是比较复杂和困难的,所以前面才大量的说明二叉树的相关的操作。

5.1、树的遍历

  树的遍历分为两种方式:

    一种是先根遍历:先访问树的根节点,然后依次在先根遍历每一个子树,同样也是一个递归的定义
    一种是后根遍历:先依次遍历每一个子树,然后依在遍历根节点,同样也是递归的定义。

  先根遍历的顺序如下图所示。
在这里插入图片描述

图5.1 先根遍历示意图

  
  按照图中 p1、p2、p3、p4、p5、… 依次进行遍历,其中节点 B 为节点 A孩子节点,但同时也是节点E 和节点F双亲结点。所以可以得到其遍历结果为:

A->B->E->F->C->G->D->H->I->J

  
  后根遍历的顺序和先跟正好相反,如下图所示。

在这里插入图片描述

图5.2 后根遍历示意图

  
  按照图中 p1、p2、p3、p4、p5、… 依次进行遍历,所以可以得到其遍历结果为:

E->F->B->G->C->H->I->J->D->A

5.2、森林的遍历

  森林的遍历也是有两种遍历顺序,分别为:前序遍历、后序遍历。

  1、先将森林中的每一个树都按照先根遍历、后根遍历的顺序遍历
  2、然后依次从左到右将每一个树的遍历结果合并到一起即为森林的遍历。
  3、树的先根遍历对应于森林的先序遍历,树的后根遍历对应森林的后序遍历

  总结:

    森林,树的前序(根)遍历和二叉树的前序遍历的结果相同
    森林、树的后根(序)遍历和二叉树的中序遍历的结果相同

  所以,当以二叉链表作为树的存储结构时,树的先根遍历和后根遍历完全可以借用二叉树的先序遍历和中序遍历的算法来实现,这其实也证实,我们找到了对树和森林这种复杂问题的简单解决办法。(摘自《大话数据结构》P200)

  
  好啦,废话不多说,总结写作不易,如果你喜欢这篇文章或者对你有用,请动动你发财的小手手帮忙点个赞,当然关注一波那就更好了,好啦,就到这儿了,么么哒(*  ̄3)(ε ̄ *)。

上一篇:数据结构(十八) – C语言版 – 树 - 二叉树的线索化及遍历 – 线索化后的直接前驱、后继获取
下一篇:数据结构(二十) – C语言版 – 树 - 霍夫曼树(哈夫曼树、赫夫曼树、最优二叉树)、霍夫曼编码

猜你喜欢

转载自blog.csdn.net/zhemingbuhao/article/details/106290013