Fork me on GitHub

树(一)

树:n(n >= 0)个结点的有限集。n = 0时为空树。在任意一棵非空树中:

  • 有且仅有一个特定的称之为根的结点。
  • 当n > 1时,其余结点可分为m(m > 0)个互不相交的有限集T1、T2、…、Tm,其中每一个集合本身又是一棵树,并且称为根的子树。

基本概念

对树的定义还需强调两点:

  • n > 0时根结点是唯一的,不可能存在多个根结点。
  • m > 0时,子树的个数没有限制,但他们一定是互不相交的。

结点分类

结点拥有的子树数称为结点的度

度为0的结点称为叶子结点终端结点

度不为0的结点称为分支结点。(除根结点外,分支结点也称为内部结点)

树的度是树内各结点的度的最大值。

结点间的关系

结点的子树的根称为该结点的孩子。该结点称为孩子的双亲

同一双亲的孩子间互称兄弟

结点的祖先是从根到该结点所经过分支上的所有结点。

以某结点为根的子树中的任一结点都称为该结点的子孙

其双亲在同一层的结点互为堂兄弟

其他概念

结点的层次从根开始定义起,根为第一层,根的孩子在第二层。

树中结点的最大层次称为树的深度或高度

如果将树中结点的各子树看成从左到右是有次序的,不能互换的,则称该树为有序树,否则称为无序树

森林是m(m >= 0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。

树的抽象数据类型

打星号内容为发博客时存有疑惑

mark

树的存储结构

双亲表示法

假设一组连续空间存储树的结点。同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。

结构代码:

1
2
3
4
5
6
7
8
9
10
11
12
#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。

mark

可以扩展出长子域或右兄弟域,存储结构的设计非常灵活。

多重链表表示法

每个结点有多个指针域,每个指针指向一棵子树的根结点。

方法一:

​ 指针域的个数等于树的度。

​ 当树中各结点的度相差很大时浪费空间。

方法二:

​ 每个结点指针域的个数等于该结点的度,专门取一个位置来存储结点指针域的个数。

​ 空间利用率高,但各个结点结构不同加上要维护结点的度的数值,运算时间上有损耗。

孩子表示法

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

mark

结构代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define MAX_TREE_SIZE 100
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;

难以找到某个结点的双亲,可以改进至双亲孩子表示法:

mark

孩子兄弟表示法

任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,可以设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。

结构代码:

1
2
3
4
5
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;

mark

同样难以找到双亲,也可以再加个parent指针。

这个表示法最大的好处是把一棵复杂的树变成了一棵二叉树。

-------------本文结束感谢您的阅读-------------
undefined