我的首发平台是公众号【CodeAllen】,学习交流QQ群:736386324
利用顺序存储和链式存储结构的特点,可以实现对树的存储结构的便是
下边主要介绍三种不同的表示法:
双亲表示法:我们假设一组连续的空间存储树的结点,同时在每个结点中,附设一个指示器指示双亲结点到链表中的位置
其中date是数据域,存储结点的数据信息,而parent是指针域,存储该结点的双亲在数组中的下标 用代码表示为:
/* 树的双亲表示法结点结构定义 */
#define MAX_TREE_SIZE 100
typedef int TElemType; /* 树结点的数据类型,目前暂定为整型 */
typedef struct PTNode /* 结点结构 */
{
TElemType data; /* 结点数据 */
int parent; /* 双亲位置 */
} PTNode;
typedef struct /* 树结构 */
{
PTNode nodes[MAX_TREE_SIZE]; /* 结点数组 */
int r,n; /* 根的位置和结点数 */
} PTree;
这样的结构定义,就可以实现双亲表示法了,因为根结点是没有双亲,约定根结点的位置域为-1,
上图树结构中的树双亲表示为
这样的存储结构,可以根据结点的parent指针很容易找到他的双亲结点,所以时间复杂度为O(1) 直到parent为-1时,表示找到了树结点的根
引出一个问题,上述结构要想知道结点的孩子是什么,需要遍历整个结构 可以增加一个结点最左边孩子的域,可以叫其长子域,如果没有孩子的结点,这个长子域就设置为-1
存储结构的设计是非常灵活的过程,一个存储结构设计的是否合理,取决于基于该结构的运算是否适合,是否方便,时间复杂度好不好等
孩子表示法:由于树中每个结点可能有多棵子树,可以考虑用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根结点,这种方法可以叫做多重链表表示法
这种树的每个孩子个数是不同的,所以可以设计两种方案解决
方案一:指针域的个数就是等于树的度
其中data是数据域,child1到childd是指针域,用来指向该结点的孩子结点
对于下边的树,树的度就是3
这种方法实现如下图:
这种方案显然有点浪费空间,既然很多指针域可能为空,为什么不按需分配呢?
于是有了方案二:每个结点指针域的个数等于该结点的度,专门取一个位置存储结点指针域的个数
其中data为数据域,degree为度域,存储的是该结点的孩子结点的个数,child1到childd是指针域,用来指向该结点的孩子结点
其实现方法是:
这种方法克服了浪费空间的问题,不过其各个结点的链表是不同的结构,加上要维护结点的度的数值,在运算上会带来时间损耗
所以想一想,有没有更好的方法?
这就引出开始说的孩子表示法
/* 树的孩子表示法结构定义 */
#define MAX_TREE_SIZE 100
typedef int TElemType; /* 树结点的数据类型,目前暂定为整型 */
typedef struct CTNode /* 孩子结点 */
{
int child;
struct CTNode *next;
} *ChildPtr;
typedef struct /* 表头结构 */
{
TElemType data;
ChildPtr firstchild;
} CTBox;
typedef struct /* 树结构 */
{
CTBox nodes[MAX_TREE_SIZE]; /* 结点数组 */
int r,n; /* 根的位置和结点数 */
} CTree;
但是这也有个问题,想知道某个结点的双亲是谁,还需要遍历才行
那就把上述的双亲表示法和孩子表示法结合一下是不是可行!
当然是可以的,这种方法就叫做
双亲孩子表示法上边的三种都是从双亲的角度和从孩子的角度研究树的结构,但是也可以从兄弟的角度分析
孩子兄弟表示法其中data是数据域,firstchild是指针域,存储该结点的第一个孩子结点的地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址
其定义代码可以表示为:
/* 树的孩子兄弟表示法结构定义 */
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
} CSNode,*CSTree;
其实现示意图可以表示为:
这种表示法,给查找某个结点的某个孩子带来了方便,只要firstchild找到此结点的长子,然后再通过长子结点的rightsib找到他的二弟,一直接下去,知道找到具体的孩子
那找结点的双亲呢?和上边类似,只要增加一个parent指针域就好了
这些都在后边的二叉树特性中体现的淋漓尽致