6.1 树的定义与存储结构(都怎么来的?)

0. 抛砖引玉

  线性表,栈与队列,串都是属于线性的关系。前后是一对一的连接,这固然有他们的好处,但对于繁杂联系的数据表示来说,显得又不够方便。

  

1. 树的定义

 试着下个定义:

 

 根据结点我们可以挖掘到哪些信息:

  1) 结点的分类

  2)结点与结点之间的关系

  3)层与层之间关系

  4)其他信息

    树与森林

 

2. 树的存储结构

 

 那么,根据上面由结点挖掘的一些信息,我们可以试着去考虑怎么去利用各结点进行数据的存储或者读取问题。

 当然,首先要去熟悉下数据的基本存储结构:【顺序存储和链式存储】。了解之后,我们来分析:

  (1)从上节结点与结点关系解释为双亲与孩子。双亲可能有多个孩子,但是每个孩纸却是只能有一个双亲结点的。抓住这一点,如果利用顺序存储结构(一维数组)的话,除了根节点root之外,每个结点数据都有其数组下标位置。那么根据某一结点便可以找到其双亲。

先定义结点的基本信息为:数据(Data) 和双亲的位置(即数组中的下标)。

 data   parents'  

再定义整个树的结构为:可以存下所有结点的连续空间——结点数组(a[max_size]), 根节点位置r_loc 和结点数目n。代码如下:

/*树的双亲表示法,结点结构定义*/
#define  max_size 100
typedef int TElemType

typedef struct TNode{
    TElemType data;     //结点数据
    int parents_loc;   //双亲数据在数组下标位置
} TNode;       

typedef struct{
    TNode a[max_size];   //结构体数组
    int r_loc, n;        //根节点位置,结点数目
}Tree;  

 

根结点位置r_loc可设为-1。以下图的树结构进行表示,可对应出其双亲的表示。

 

这样,便实现两个主要功能:(1)数据的存储 (2)由任一个结点(孩子)找到其双亲。

譬如知道孩子Data: a[4].data = E,可获得其parents'loc: a[4].parents_loc = 2。再根据a[2]得到其双亲的值为C。

 

我们可以再继续扩展其功能。由孩子找到双亲是ok的,反过来,我们也希望找到结点的孩子。但由于孩子有很多呀,比如G、H、I。

我们就选定第一个孩子(从左到右边),容易确定。添加一个firstchild信息。如果是叶子结点的话,意味着没有孩子。则设置为-1。

 

 再扩展的,可以横向来看,考虑结点的第一个兄妹,比如G->H,D->E。同样结点没有兄妹的情况,设定为-1。

 

当然,扩展这些功能也带来一些弊端:

a)考虑得多,树结构存储结构也就变得更复杂,不方便;

b) 由于是顺序存储结构(数组的局限性),树的扩充等操作比较受限。

 

最后,上面谈到树的这种结构表示,一般书上面都称作:双亲表示法

 

 (2)我们上面由孩子有唯一的双亲的角度出发,并做了一些扩展。现在我们完全反过来,双亲有很多孩子,看起来有点麻烦,但我们能不能也实现出来。问题是这么多孩子,比如D—>G,H,I。该如何把孩子们串起来?

这就利用单链表的作用了。结合上面顺序存储结构的数组,联合实现。代码如下:

/*树的孩子表示法,结点结构定义*/
#define  max_size 100
typedef int TElemType

typedef struct CTNode{
  int child;
  struct CTNode* next;
} *CTNode; typedef struct TNode{  //表头 TElemType data; //结点数据 CTNode firstchild; } TNode; typedef struct{ TNode a[max_size]; //结构体数组 int r_loc, n; //根节点位置,结点数目 }Tree;

 

 

 

采用这种结构,能够实现对每个结点的孩子,以及孩子的兄妹们的查找。一般称这种结构为:孩子表示法

当然,孩子表示法的不足在于没有第一种方案的查找结点的双亲的功能。

 

这个结合第(1)(2)种,组合成双亲孩子表示法或者孩子双亲表示法。也就需要更复杂点的操作了。不再展开。

 

(3)我们第(1)种扩展后,用到了firstchild和firstbro,同时采用的是数组下标的方式。我们这里改用链表指针的方式,进行调整。firstchild和firstbro分别代表指针。前者是结点指向第一个孩子,

后者是从第一个孩子指向其第一个兄弟。来实现孩子兄弟表示法或者我觉得可以叫孩子兄妹表示法

/*树的孩子兄弟表示法*/
typedef int TElemType;
typdef struct TreeNode{
    TElemType data;
    struct TreeNode  *firstchild, *firstbro;
}TreeNode, *LinkedTree;

这种结构,也是容易找到第一个孩子,以及孩子的第一兄弟。如果要找双亲的话,可以再添加一个指向双亲的指针。

值得一提的是,孩子兄弟表示法,是把一棵树转换成二叉树结构形式。进而可以方便利用二叉树的性质和特点来处理树。

 

 综上而言,(1)和(2)主要是从孩子到双亲,以及从双亲找所有孩子的方式,同时利用顺序存储即数组实现数据存储,以及利用下标进行索引。

(3)则从结点第一个孩子和孩子的第一个兄弟角度,充分利用链表指针进行结构的表示,相比来说也比较简洁些,同时也是二叉树的基础,所以这种存储结构的应用比较多些。

 

Reference

(1) 《大话数据结构》,本文终究还是没有挣脱它的结构呀~ 

 



posted @ 2018-11-07 20:07  刀亘  阅读(389)  评论(0编辑  收藏  举报