一.定义:
1. 树是 n(n>=0) 个节点的有限集。n=0 时成为空树。在任意一棵非空树中:有且只有一个特定的成为根节点。其余m(m>0)个互不相交的有限集 T1,T2……Tm,其中每一个集合本身又是一棵树,并且成为根的子树。并且子树是不相交的。
2. 度:节点拥有的子树的个数,度为 0 的节点为叶节点,度不为 0 的节点成为非终端节点或分支节点 。
3. 树的度是树内各节点度的最大值。
4. 节点的子树的根称为该节点的孩子,该节点称为孩子的双亲
5. 同一个双亲的孩子之间互称为兄弟。
节点的祖先是从根到该节点所经分支上的所有节点
6 节点的层次从根开始定起,根为第一层,其双亲在同一层的节点互为堂兄弟。
7. 树中结点的最大层次称为树的深度。
8. 有序树:树中节点的各子树看成从左到右是有次序的,不能互换
9. 森林:m(m>=0) 棵互不相交的树的集合。
二.树的存储结构
1. 双亲表示法:以一组连续空间存储数的节点,同时每个节点中,附设一个指示器指示其双亲节点在数组中的位置。
//结构定义
typedef int TElemType;
typedef Struct PTNode{
TElemType data; // 存储的数据
int patent; // 存储的双亲的位置
} PTNode;
typedef struct{ // 树结构
PTNode nodes[MAX_TREE_SIZE];
int r,n;
// 根的位置和节点数
}
找到双亲节点的时间复杂度:O(1)
找到节点的孩子的时间复杂度:O(n)
改进一:增加长子域(该节点的最左边孩子的域)
改进二:增加右兄弟域
2.孩子表示法:
每个节点有多个指针域,其中每个指针指向一棵子树的根节点,称为多重链表表示法。
方案一:
根据树的度来确定每个节点指针域的个数。如图中树的度为 4 ,则每个节点都有 4 个孩子域
缺点:当树中各节点的度相差很大时,浪费空间,因为很多节点的指针域是空的。
方案二:
每个节点指针域的个数等于该节点的度,专门取一个节点的位置来存储节点指针域的个数。如根结点有两个孩子,则有两个孩子领域,一个数字域。
缺点:克服了空间上的浪费,但由于每个节点的链表是不相同的结构,还要维护 节点的度的数值,在时间会有损耗。
引入即减少空指针的浪费又能使节点的结构相同
孩子表示法:把每个节点的孩子结点排列起来,用单链表作为存储结构,n 个节点 n 个链表,如果为叶子节点,该单链表为空。再将 n 个头指针组成一个线性表,采用顺序存储结构,存放到一维数组中。
//树的孩子节点表示法结构定义
#define MAX_TREE_SIZE 100
typedef struct CTNode //孩子节点
{
int child ;
struct CTNode *next;
}*ChildPtr;
typedef struct{ //表头结构
TlemType data;
ChildPtr firstChild;
}CTBox;
typedef struct{ //树的结构
CTBox nodes[MAX_TREE_SIZE];
int r,n;//根的位置和节点数。
}Ctree;
问题:对于查找每个节点的孩子和其孩子节点的兄弟,只需查找这个节点的孩子的单链表就可以了,但想要知道每个节点的双亲是谁需要遍历整棵树。
进而有了:孩子双亲表示法。
//树的孩子节点表示法结构定义
#define MAX_TREE_SIZE 100
typedef struct CTNode //孩子节点
{
int child ;
struct CTNode *next;
}*ChildPtr;
typedef struct{ //表头结构
int parentIndex;
TlemType data;
ChildPtr firstChild;
}CTBox;
typedef struct{ //树的结构
CTBox nodes[MAX_TREE_SIZE];
int r,n;//根的位置和节点数。
}Ctree;
孩子兄弟表示法:任意一棵树,它的节点的第一个孩子如果存在就是唯一的,它的右兄弟也是唯一的。因此设置两个指针,分别指向该节点的第一个孩子和此节点的右兄弟。
typedef struct CSNode{
TElemType data;
struct CSNode * first child, *rightsib;
}CSNode, *CSTree;
好处:查找某个节点的孩子带来了方便,并把一个复杂的树变成了一棵二叉树。