树
基本概念
定义:
树( Tree)是 n(n>=0) 个结点的有限集。n=0时称为空树。在任意一棵非空树中:
1.有且仅有一个特定的称为根 (Root)的结点;
2.当 n>1 时,其余结点可分为 m (m>O) 个互不相变的有限集 T1 、T2、……、Tm ,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
性质:
1.n>0时根节点是唯一的;不存在多个根节点;
2.m>0时子树的个数没有限制,但是他们一定是互不相交的。
结点分类
结点的度:该结点拥有的子树的数目
终端结点(叶结点):结点度为0的结点
分支结点(非终端结点):度不为0
内部结点:除根以外的分支结点
树的度:树内各节点的度的最大值
图为转载
结点间关系
结点的子树的根称为该节点的孩子,相应的该结点称为孩子的双亲,同一个双亲的孩子之间互称兄弟,结点的祖先是从根到该节点所经分支上的所有结点,以某结点为根的子树中的任一结点都称为该结点的子孙。
其他概念
结点层次:从根开始定义,根为第一层,根的孩子为第二层,依次递加
树的深度(高度):树中结点的最大层次
有序树:蜀中结点的各子树看成是从左至右有次序,不可互换的。否则称为无序树
森林:m(m>=0)棵互不相交的树的集合
树的抽象类型
ADT 树(tree)
Data
树是由一个根结点和若干棵子树构成 。 树中结点具有相同数据类型及层次关系。
Operation
lnitTree ( *T) ; //构造空树T
DestroyTree ( *T ); //销毁树T
CreateTree(*T,definition); //按definition中给出树的定义来构造树
ClearTree(*T); //若树T存在,清空树T
TreeEmpty(T); //若树为空树,返回true,否则返回false
TreeDepth(T); //返回T的深度
Root(T); //返回T的根结点
Value(T,cur_e); //cur_e为树中的一个结点则返回该结点值
Assign(T,cur_e,value); //给树T的结点cur_e赋值value
Parent(T,cur_e); //若cur_e是树T的非根结点,则返回他的双亲,否则返回false
LeftChild(T,cur_e); //若cur_e是树T的非叶结点,则返回它的最左孩子,否则返回空
RightSibling(T,cur_e); //若cur_e有右兄弟,则返回其右兄弟,否则返回空
InsertChild(*T,*p,i,c); //其中p指向树T的某个结点,i为所指结点p的度加上1,非空树c与T不相交
//操作将插入c为树T中p指结点的第i棵子树
DeleteChild(*T,*p,i); //其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指结点的第i棵子树
endADT
树的存储结构
一、双亲表示法
每个结点中包含一个数据域和一个指示器。
指示器指示其双亲结点到链表中的位置,结合数组存储即为其双亲结点的下标,根结点的双亲位置设为-1
结构定义
#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; //根的位置 和 结点数
}
数组下标 | 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 |
以上结构在于不容易找到结点的孩子,必须遍历整个结构才行,所以在以上基础进行改进。
改进一:增加一个指针域,指向该结点最左边的孩子,若没有孩子则设为-1
数组下标 | data | parent | firstchild |
0 | A | -1 | -1 |
1 | B | 0 | 3 |
2 | C | 0 | 4 |
3 | D | 1 | 6 |
4 | E | 2 | 9 |
5 | F | 2 | -1 |
6 | G | 3 | -1 |
7 | H | 3 | -1 |
8 | I | 3 | -1 |
9 | J | 4 | -1 |
改进二:再增加一个指针域,指向该结点的右兄弟,如果没有右兄弟则设为-1
数组下标 | data | parent | firstchild | rightsib |
0 | A | -1 | -1 | -1 |
1 | B | 0 | 3 | 2 |
2 | C | 0 | 4 | -1 |
3 | D | 1 | 6 | -1 |
4 | E | 2 | 9 | 5 |
5 | F | 2 | -1 | -1 |
6 | G | 3 | -1 | 7 |
7 | H | 3 | -1 | 8 |
8 | I | 3 | -1 | -1 |
9 | J | 4 | -1 | -1 |
该存储结构可以根据需求不断增减指针域来符合设计要求。
二、孩子表示法
每个结点有多个指针域,其中每个指针指向一棵子树的根结点,我们把这种方法叫做多重链表表示法。
但是由于每个结点的度不同,指针域的大小也不同,故存在一些问题。
最终常采用方法结合顺序结构和链表结构,详细如下:
把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构存进一个一维数组。
结构定义
#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
以上结构还是存在不知道结点双亲的情况,所以可以改进,在表头结点中加入parent指针域,结合之后称为双亲孩子表示法
三、孩子兄弟表示法
任意一棵树,他的结点的第一个孩子如果存在就是唯一的,他的右兄弟存在也是唯一的。因此可以设置两个指针,分别指向该节点的第一个孩子和他的右兄弟。
结构定义
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;
同样如果有必要完全可以再增加一个parent指针,指向其双亲的位置。
而上述图的结构即为二叉树
注:以上图片均为转载网络图片