数据结构(16)树与森林

本文介绍了树的三种存储结构:双亲表示法、孩子表示法和孩子兄弟表示法,详细阐述了它们的优缺点。同时,文章讨论了如何将树转换为二叉树,以及森林转换为二叉树的过程。最后提到了孩子兄弟树的实现及其在查找父结点时的注意事项。
摘要由CSDN通过智能技术生成

前言

树是n(n>=0)个结点的有限集。在任意一棵非空树中:1.有且只有一个特定的称为根的结点 2.当n>1时,其余结点可分为m(m>=0)个互不相交的有限集,并且称为根的子树。可以发现,在树的定义中,又用到了树的概念,树的结构定义是一个递归的定义。

森林则是m(m>=0)棵互不相交的树的集合。对树中的每一个结点而言,其子树的集合即为森林。因此,也可以用森林和树相互递归的定义来描述树。

树的应用很广泛,但是由于树的形状可以任意,显然对树的处理会复杂许多。

树的存储结构

双亲表示法

在双亲表示法中,结点拥有一个数据域和一个指针域,其中指针域指向结点的双亲结点。

(注:”双亲“结点即父结点,并不意味着两个有结点,搞不懂为啥要这样翻译)

这里我们以顺序存储为例子,假设用一个数组来保存整棵树,那么数组下标就是结点的位置。

img_1

如果用这种方式,那么结点的结构如下:

typedef struct PTNode{
   
  //数据域
  ElemType data;
  //指针域
  int parent;
}PTNode;

使用这种表示法的缺点也很明显:假如想得到结点的孩子,需要遍历整个结构才能得到。

孩子表示法

顾名思义,孩子表示法即指针域指向的是结点的孩子结点。但是与二叉树不同,一棵树可以拥有多个结点,那么指针域该如何设计呢?

第一种方法是采用固定指针域数量的结点,也就是说结点的格式是一致的。

img_2

typedef struct CTNode{
   
  //数据域
  ElemType data;
  //指针域
  int child1;
  int child2;
  //...
  int childd;
}CTNode;

这种方式的弊端很明显:假设某个结点的孩子结点数量很多,而其他结点的孩子结点数量很少时,由于采取了统一格式,所有结点的指针域数量必须同最大的数量一致,这样就造成了浪费。

因此可以采取第二种方式:增加一个数据域用于记录结点的度,根据度来确定指针域的个数。

img_3

typedef struct CTNode{
   
  //数据域
  ElemType data;
  //结点的度
  int degree;
  //指针域
  int child1;
  //...
}CTNode;

显然,这种方法虽然能节约空间,但是每个结点的结构不一样,操作上很不方便。

有一种改进方法是,将每个结点的孩子结点排列起来,看成一个线性表,以单链表作为存储结构。

img_4

这样,我们需要设计一个孩子结点,作为该链表存储的对象。

//孩子结点
typedef struct ChildNode{
   
  //孩子结点的位置
  int child;
  //指向下一个孩子结点
  struct ChildNode* next;
}ChildNode;

树的结点:

//树的结点
typedef struct CTNode{
   
  //数据域
  ElemType data;
  //指针域-孩子结点
  ChildNode* firstChild;
}CTNode;

采用这种方式很容易找到孩子结点,但是不容易找到父结点。可以与双亲表示法结合起来,使操作更简便。

孩子兄弟表示法

孩子兄弟表示法又称为二叉树表示法,即用二叉链表作为树的存储结构。这样,树的结点一定有两个指针域,规定将左结点的指针域指向该结点的第一个孩子结点,右结点的指针域指向结点的下一个兄弟结点,得到孩子兄弟表示法的结点结构如下:

typedef struct CSNode{
   
  //数据域
  ElemType data;
  //指针域-左结点指向第一个孩子结点
  struct CSNode* firstChild;
  //指针域-右结点指向下一个兄弟结点
  struct CSNode* nextSibling;
}CSNode;

用孩子兄弟表示法来表示树,可以看做是将一棵树转换为二叉树。

树、森林与二叉树的转换

树转换为二叉树

前面提到,孩子兄弟表示法即使用二叉链表来存储树。

作为二叉链表,除了数据域外,它必定只有两个指针域,指向其左结点和右结点。

在二叉树中,左结点指向的是左子树,右结点指向的是右子树。

在线索二叉树中,左结点指向左子树或者前驱,右结点指向右子树或后继。

在树中,没有左右子树的说法,那么用左结点指向它的第一个孩子结点,右结点指向它的下一个兄弟结点。

示例:

img_5

  • 首先存储R,其第一个孩子结点为A,则R的左结点为A;R无兄弟结点,右结点为空
  • A的第一个孩子结点为D,则A的左结点为D;A的下一个兄弟节点为B,则其右结点为B
  • D无孩子结点,则其左结点为空;D的下一个兄弟结点为E,则其右结点为E
  • E无孩子结点,则其左结点为空;E无下一个兄弟,则其右结点也为空
  • B无孩子结点,则其左结点为空;B的下一个兄弟结点为C,则其右结点为C
  • C的第一个孩子结点为F,则其左结点为F;C无下一个兄弟,则其右结点为空
  • F的第一个孩子结点为G,则其左结点为G;F无兄弟,则其右结点为空
  • G无孩子结点,则其左结点为空;G的下一个兄弟结点为H,则其右结点为H
  • H无孩子结点,则其左结点为空;H的下一个兄弟结点为K,则其右结点为K
  • K无孩子结点,则其左结点为空;K无下一个兄弟,则其右孩子为空

img_6

由此可见,在物理结构上,二叉树与孩子兄弟表示法的树是一致的,只是解释方法不同,含义就不同了。

由于根结点没有兄弟,因此根结点的右结点必为空。

森林转换为二叉树

森林是由若干棵树组成的,因此每一棵树都能各自转换为二叉树。同时可以认为,森林中的每一棵树都是兄弟,即根结点的兄弟结点是下一棵树的根结点。基于这两点,森林转换为二叉树是很容易的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值