假设一组连续的空间存储树的特点。同时在每个结点中,附设一个指示器指示双亲结点到链表中的位置。 也就是说,每个结点除了知道自己是谁以外,还知道他的双亲在哪里,他的结点结构图1-1所示。
//树的双亲表示法结点结构定义
#define MAX_TRUE_SIZE 100
typedef int TElemType //树结点的数据类型
//结点结构
typedef struct PTNode
{
TElemType data; //结点数据
int parent; //双亲位置
}PTNode
//树结构
typedef struct
{
PTNode nodes[MAX_TRUE_SIZE]; //结点数组
int r,n //根的位置和结点数
}PTree
有了这样的数据结构就可以来实现双亲表示法。由于根结点是没有双亲的,所以我们约定根结点的位置域设置为-1,这也就意味着,我们所有的结点都存有他双亲的位置。如图1-2中的树结构和表1-3中的树双亲表示。
改进一下,增加一个结点最左边孩子的域,不妨叫他长子域,这样就可以很容易得到结点的孩子。如果没有孩子的结点,这个长子域就设置为-1,如表1-4所示。
另外一个问题,我们很关注各兄弟之间的关系,双亲表示法无法体现这样的关系,可以增加一个右兄弟域来体现兄弟关系,也就是说,每个结点如果他存在右兄弟,则记录下右兄弟的下标。同样的,如果右兄弟不存在,则赋值-1.表1-5所示。
换一种完全不同的考虑方法,由于树中每个结点可能有多棵子树,可以考虑用多重链表。每个结点有多个指针域,其中每个指针指向一颗子树的根结点,我们把这种方法叫做多重链表的表示方法。 不过,树的每个结点的度,也就是他的孩子个数是不同的,所以设计两种方法:
方案一:
指针域的个数就等于树的度,树的度就是树各个结点度的最大值。其结构如图1-6所示。
方案二:
每个结点指针域的个数等于该结点的度,我们专门取一个位置来存储结点指针的个数。如图1-8所示。
这就是我们要说的孩子表示法,把每个结点的孩子都排列起来,以单链表为存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组,如图1-10所示。
//树的孩子表示法结构定义
#define MAX_TRUE_SIZE 100
typedef struct CTNode //孩子结点
{
int child;
struct CTNode *next;
}*ChildPtr;
//表头结构
typedef struct
{
TElemType data;
ChildPtr firstchild;
}CTBox;
//树结构
typedef struct
{
CTBox nodes[MAX_TRUE_SIZE]; //结点数组
int r,n; //根的位置和结点数
}CTree
这样的结构对于我们要查找某个结点的某个孩子,或者找某个结点的兄弟,只需要查找这个结点的孩子单链表即可。对于遍历整棵树也是很方便的,对于头结点的数组循环即可。
但是这也存在问题,我们如何知道某个结点的双亲是谁了?比较麻烦,需要调整整颗遍历树才行,难道就不可以把双亲表示法和孩子表示法综合一下吗?当然是可以的。图1-13所示。
任一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此。我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。 结点结构表图1-14所示。
//孩子兄弟表示法结构定义
typedef struct CSDNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;