树的定义
树(Tree)是N(N>=0)个结点的有限集。当N=0时成为空树,在任意一棵非空树种:
-有且仅有一个特定的成为根(Root)的结点。
-当N>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2...Tm,其中每一个集合本身又是一棵树,并且成为根的子树(SubTree)。
虽然从概念上很容易理解树,但是有两点还是需要大家注意下
-n>0时,根结点是唯一的,坚决不可能存在多个根结点。
-m>0时,子树的个数是没有限制的,但他们互相是一定不会相交的。
结点分类
上图中,每一个圈圈我们就称为树的一个结点。结点拥有的子树称为结点的度-(Degree),树的度取树内各结点的度的最大值。
-度为0的结点称为叶结点(Leaf)或终端结点。
-度不为0的结点称为分支结点或非终端结点,除根结点外,分支结点也称为内部结点。
结点间的关系
结点的子树的根称为结点的孩子(Child),相应的,该结点称为孩子的双亲(Parent),同一双亲的孩子之间互称为兄弟(Sibling)。
结点的祖先是从根到该结点所经分支上的所有结点。
其他概念
如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树。
森林是m(m>=0)棵互不相交的树的集合。对树种每个结点而言,其子树的集合即为森林。
结点的层次
结点的层次(Level)从根开始定一起,根为第一层,根的孩子为第二层,
其双亲在同一层的结点互为堂兄弟。
树中结点的最大层次称为树的深度(Depth)或高度。
树的存储结构
要存储树,简单的顺序存储结构和链式存储结构是不能的,不过如果充分利用它们各自的特点,完全可以间接地来实现。要考虑到双亲,孩子,兄弟之间的关系。
这里介绍三种不同的表示法:双亲表示法,孩子表示法,孩子兄弟表示法。
双亲表示法
言外之意就是以双亲作为索引的关键词的一种存储方式。
我们假设以一组连续控件存储树的结点,同事在每个结点中,附设一个指示其双亲结点在数组中位置的元素。也就是说,每个结点除了知道自己是谁之外,还要知道它的爸爸妈妈在哪里。
这样的存储结构,我们可以根据某结点的parent指针找到它的双亲结点,所用的时间复杂度是O(1),索引到parent的值为-1时,表示找到了树结点的根。
可是如果我们要知道某节点的孩子是什么?那么不好意思,请遍历整个树结构。能不能改进一下呢?
当然,我们可以这样改进下:
那如果我们又比较它们兄弟之间的关系呢?
存储结构的设计是一个非常灵活的过程,只要你愿意,你可以设计出任何你想要的结构。
一个存储结构设计的是否合理,取决于基于该存储结构的运算是否适合,是否方便,时间复杂度好不好等等。
孩子表示法
这里给出一共三种方案,第一种:根据树的度,声明足够空间存放子树指针指针的节点。
但是这种方式呢,很明显造成资源的浪费。
针对方案一的缺点,我们有了方案二:
这样我们就克服了浪费这个概念,我们从此走上了节俭的社会主义道路,但每个节点的度的值不同,初始化和维护起来难度巨大,结构动态很复杂,花费了更多的时间。
下面这一种即节省了空间又节省了时间。
这种是数组和链表的搭配构成。但是上面只能找到孩子,找不到双亲貌似还不够完善,那么合并双亲孩子表示法:
那么在最后还有一种孩子兄弟表示法,构造方式也是大同小异,不多做解释,大家可以来思考。
那么上代码来写结构: