树(Tree): n(n≥0)个结点构成的有限集合。对于任一棵非空树(n> 0),它具备以下性质:1、树中有一个称为"根(Root)"的特殊结点;2、其余结点可分为m(m>0)个互不相交的有限集,其 中每个集合本身又是一棵树,称为原来树的" 子树(SubTree)"
子树是不相交的;除了根结点外,每个结点有且仅有一个父结点;一棵N个结点的树有N-1条边。
一、树的一些基本术语
1、 结点的度(Degree): 子树的个数,也就是,结点有几条边,度就是几;
2、 树的度:树的所有结点中最大的度数;
3、 叶结点:度为0的结点;
4、 父结点:有子树的结点,是其子树的根结点的父结点;
5、 子结点:若A结点是B结点的父结点,B结点是A结点的子结点,也称孩子结点;
6、 兄弟结点:具有同一父结点的各结点,彼此是兄弟结点;
7、 路径:从结点n1到nk的路径,为一 个结点序列n1 , n2 ,… , nk , ni,是 ni+1的父结 点;
8、 路径长度:路径所包含边的个数为路径的长度;
9、 祖先结点:沿树根到某一结点路径上的所有结点都是这个结点的祖先结点;
10、子孙结点:某一结点的子树中的所有结点是这个结点的子孙;
11、结点的层次:规定根结点在1层, 其它任一结点的层数是其父结点的层数加1;
12、树的深度:树中所有结点中的最大层次;
二、树的深度与高度
深度是从根结点向下,到某结点的唯一路径的长,高度是某结点向下,到某个叶结点最长简单路径中边的条数。对于整棵树来说,最深的叶结点的深度就是树的深度,树根的高度就是树的高度,这样树的高度和树的深度是相等的。根的深度为0,所有的树叶的高度都为0,对于树中相同深度的每个结点来说,它们的高度不一定相同,这取决于每个结点下面的叶结点的深度。
三、树的结点数 = 边+1,即一个有N结点的的树有N-1条边。
1、已知一棵度为4的树中,其度为0、1、2、3的结点数分别为14、4、3、2,求该树的结点总数n和度为4的结点个数,并给出推导过程。
1.) 设度为4的结点为x,那么,(14+4+3+2) + x == (1*4+2*3+3*2+4*x) + 1,x为2,结点总数为:(14+4+3+2) + 2 == 25
2.) n0 = 1 + n2 + 2 * n3 + 3*n4;14 = 1 + 3 + 2*2 + 3*n4,n4为2,结点总数为:(14+4+3+2) + 2 == 25
2、在一棵度为3的树中,度为3的节点个数为2,度为2的节点个数为1,则度为0的节点个数是多少?请给出求解过程。
n0+n1+n2+n3-1 == 0*n0 + 1*n1 + 2*n2 + 3*n3 ;n0 = 1 + n2 + 2 * n3;代入题目给出的条件,n0 = 1+1+2*2 = 6
这种树的形状是多样的,但是不影响计算结果,与度为1的条件无关。
四、普通树(General Trees),普通树的兄弟结点数目是不受限制的,森林是由一颗或多颗树组成的集合。
1、普通树的实现,结点及边的存储和计算,两种方式,一种是基于数组,一种是基于链表。
基于数组的实现,采取父指针数组(Parent pointer Array)的存储方法,先定义一个数组存储每一个结点,这样每个结点都有一个序号,再定义一个整数的数组存储结点父结点的序号(树根是没有父亲的,可以设置成一个特殊的值)。采取父指针数组也可以实现多颗树(森林)的存储,只是根结点不止一个。
基于链表的实现,是采取左儿子右兄弟的存储方法, 森林是每一颗普通树采取左儿子右兄弟实现,然后把每棵树的树根右兄弟的方式连接起来。
2、普通树的遍历,普通树主要有先根遍历和后根遍历;
先根遍历:若树非空,则先访问根结点,再按照从左到右的顺序遍历根结点的每一棵子树;普通树的先根遍历与这棵树对应的二叉树的先序遍历相同;
后根遍历:若树非空,则按照从左到右的顺序遍历根结点的每一棵子树,之后再访问根结点;普通树的后根遍历与这棵树对应的二叉树的中序遍历相同。
3、森林的遍历,只有二叉树森林才有中序遍历与完全二叉树中序遍历相同,普通的树构成的森林是不存在中序遍历;森林的先根遍历与二叉树的先序遍历相同,森林的后根遍历与二叉树的中序遍历相同;
森林,树,二叉树可以相互转换
五、二叉树:度为2的树;采取儿子兄弟法,可以把普通树改为二叉树
六、二叉树结点定义
1 typedef struct TNode *Position; 2 typedef Position BinTree; /* 二叉树类型 */ 3 struct TNode{ /* 树结点定义 */ 4 ElementType Data; /* 结点数据 */ 5 BinTree Left; /* 指向左子树 */ 6 BinTree Right; /* 指向右子树 */ 7 };