12、树的存储

 

一、课程目标

了解树的三种存储结构,能够编码实现树的存储以及访问等操作。

二、目标详解

说到存储结构,无外乎顺序存储以及链式存储两种。就顺序存储而言,在内存中用一段连续的存储单元依次存储线性表的数据元素,如数组、栈、队列等。对于线性数据结构来说,顺序存储是很自然的,但是对于树这种一对多的结构呢?

树中某个结点的孩子可以有多个,这就意味着,无论按照何种顺序将树中所有结点存储到数组中,结点的存储位置都无法直接反映逻辑关系。如果用顺序结构,数据元素挨个的存储,那么谁是谁的双亲谁是谁的孩子呢?因此简单的顺序存储结构是不能满足树的实现要求的。

对于树的存储,要充分利用顺序存储和链式存储的特点,我们这里介绍三种常用的邻接表的存储方法:双亲表示法、孩子表示法、双亲孩子表示法。

下图为要存储的示例树的逻辑结构:

1. 双亲表示法

在树中,结点可能会没有孩子,但是除了根节点外,每个结点都会有一个确定的双亲。我们假设以一组连续空间存储树的结点,同时在每个节点中,附设一个指示器指出其双亲结点在链表中的位置,也就是说,每个节点除了知道自己是谁以外,还知道它的双亲在哪里。各结点的结构如下所示:

dataparent
数据域:结点数据信息指针域:存储双亲结点位置

代码实现:

#define Max_Node_Num 100  // 结点最大个数

struct Node  // 结点结构
{
  char data;  // 数据域,类型可变
  int parent;  // 指针域,双亲位置
};

struct Tree  // 树结构
{
  Node nodes[Max_Node_Num];  // 结点数组
  int root;  // 根节点位置
  int num;  // 结点数
};

下图为示例树的双亲表示法的存储结构:

双亲表示法的特点:

  • 由于根节点是没有双亲的,一般约定根节点的指针域值为-1。
  • 根据结点的parent指针,可以很容易的找到它的双亲结点。所用时间复杂度为O(1),知道parent值为-1时,表示找到了树的根。
  • 如果想要找到孩子结点,需要遍历整个结构才行。

2. 孩子表示法

把每个结点的孩子排列起来,以单链表作为存储结构,则n个结点有n个孩子链表,如果是叶子结点则此链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中,结点结构如下:

  • 表头数组的表头结点
data(数据域)firstchild(头指针域)
存储某个结点的数据信息存储该节点的孩子链表的头指针
  • 孩子链表的孩子结点
child(数据域)next(指针域)
存储某个结点在表头数组的下标存储指向某节点的下一个孩子结点的指针

代码实现:

#define Max_Node_Num 100  // 结点最大个数

struct Child  // 孩子结点
{
  int child;  // 孩子结点的下标
  Child* next;  // 下一个孩子结点的指针
};

struct Box  // 表头结构
{
  char data;  // 数据域:存放各结点数据
  Child* firstchild;  // 指向第一个孩子结点的指针
};

struct Tree  // 树结构
{
  Box nodes[Max_Node_Num];  // 结点数组
  int root;  // 根节点位置
  int num;  // 结点数
};

下图为示例树的孩子表示法的存储结构:

对于孩子表示法,查找某个结点的某个孩子,或者找某个结点的兄弟,只需要查找这个结点的孩子单链表即可。但是当要寻找某个结点的双亲时,就不是那么方便了。所以可以将双亲表示法和孩子表示法相结合,形成双亲孩子表示法。

3. 双亲孩子表示法

在孩子表示法的表头结构中添加存储结点双亲位置的指针,变为如下结构:

dataparentfirstchild
存储某个结点的数据信息存储双亲结点位置存储该节点的孩子链表的头指针

代码实现:

#define Max_Node_Num 100  // 结点最大个数

struct Child  // 孩子结点
{
  int child;  // 孩子结点的下标
  Child* next;  // 下一个孩子结点的指针
};

struct Box  // 表头结构
{
  char data;  // 数据域:存放各结点数据
  int parent;  // 存放双亲结点的位置
  Child* firstchild;  // 指向第一个孩子结点的指针
};

struct Tree  // 树结构
{
  Box nodes[Max_Node_Num];  // 结点数组
  int root;  // 根节点位置
  int num;  // 结点数
};

下图为示例树的双亲孩子表示法的存储结构:

4. 孩子兄弟表示法

各节点的指针域存放子节点和兄弟节点的指针。

示例树的孩子兄弟表示法的存储结构:

三、扩展

由于树可以看成是一张无向图,在信奥赛中,涉及到树的题目时,常用邻接矩阵的形式存储树的结构,若树的结点数为n,则设立一个n*n的二维数组a,在数组a中,若a[i][j]的值为0则表示i结点不是j结点的双亲结点(没有边相连接);若a[i][j]值为1则表示i号结点是j号结点的双亲结点(有边相连接),这样也能存储下来一颗树,但是在树的结点较多时,二维数组中存在大量的无效数据,空间浪费较多,此时应使用领接表的存储方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值