我们先来了解一下,什么是树;
树:树(Tree)是n(n>=0)个节点的有限集。n=0时称为空树。在任意一颗非空树中:(1)有且仅有一个特定的称为根(Root)的节点:(2)当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1、T2、······、Tn,其中每一个集合本身又是一颗树,并且称为根的子树(SubTree)。
下来了解一下学习树会用到的一些关键词;
结点:结点拥有的子树称为结点的度。度为0的结点称为叶节点或终端结点;度不为0的结点称为非终端结点或分支结点。除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。如下图所示,因为这棵树结点的度的最大值是结点D的度,为3,所以数的度也为3。
节点间关系 :结点的子树的根称为该结点的孩子,相应地,该结点称为孩子的双亲。同一个双亲的孩子之间互称兄弟。结点的祖先是从根到该结点所经分支上的所有结点。
树的层次:结点的层次从根开始定义起,根为第一层,根的孩子为第二层。若某结点在第I层,则其子树的根就在第I+1层。其双亲在同一层的结点互为堂兄弟。树的结点的最大层次称为树的深度或高度,下图所示树的深度为4。
如果将树中结点的的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树;
森林是m(m>=0)颗互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。
下面来说一下树在计算机中最常用的三种表示方法:
- 双亲表示法
一个人可以没有孩子,但是他一定有且仅有一个双亲。我们假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。就是说结点除了知道自己是谁以外,还知道他的双亲在哪里。结构如下表所示。
data | parent |
---|
其中data是指针域,存储结点的数据信息 。而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;
用上面的结构定义我们就可以把下面的树表示为下表所示的定义方法:
下标 | data | parent |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 1 |
4 | E | 2 |
5 | F | 2 |
6 | G | 3 |
7 | H | 3 |
8 | I | 3 |
9 | J | 4 |
这样存储树,我们可以根据parent指针很容易找到他的双亲结点,时间复杂度为O(1)。但是如果要找一个结点的孩子,就要遍历整个结构才行,时间复杂度瞬间变为O(n);但是我们可以再此的基础上再加一个指针域,指向他的左孩子,如下表所示:
下标 | data | parent | firstchild |
---|---|---|---|
0 | A | -1 | -1 |
1 | B | 0 | 3 |