本文部分图片截取自B站懒猫老师。
视频链接为:https://www.bilibili.com/video/BV1o541147mS?spm_id_from=333.999.0.0.
树的定义
树:n个结点的有限集合。当 n = 0 时,称为空树。
任意一颗非空树满足以下条件:
(1)有且仅有一个特定的称为根的结点。
判断下图中是否是树的结构。
计算机磁盘存储。
A-B-E-L 经过三条边,所以路径长度就是 3.
从 A-B-E-L 这条路径来看,A是B、E、L的祖先,B、E、L是A的子孙.
数据结构中一般讨论的都是有序数。
如果把上图中的根节点 A 给去掉,它的三颗子树就变成了森林。
树的存储结构
方法一:双亲表示法
通过查找该数组元素(结点)的parent的值,来确定该结点的双亲结点。不需要用到循环,所以花费时间很少。
通过循环来遍历每个结点的parent的值,然后将该值与数组下标(结点所对应的层数)进行比较,从而来确定孩子结点。时间会慢一些。
如何让查找孩子结点变的快捷呢?
我们可以在结构体中增加一个字段,比如下图所示。
firstChild表示第一个孩子结点。
这里只记录了一个孩子,那如果想获知其他孩子结点怎么办呢?
这就要涉及到如何去查找兄弟结点了。
我们还可以在结构体中增加一个字段用来表示兄弟结点的信息,比如下图。
其中,rightSib的意思是右同胞的意思。
通过增加rightSight字段,我们可以发现是可以将这颗树的信息给描述出来的。其中,当发现rightSib中的值为-1时,就表示没有右边没有兄弟结点了。相当于链表中的空指针用来表示链表的结束的概念。
方法二:孩子链表表示法
回顾下,
优点:直观
缺点:浪费存储空间
增加了一个度域,用来存放该结点的度。
优点:直观
缺点:结点结构不一致,会给编码带来不便。因为不同结点结构要定义不同的表示该结点的结构体类型
所以说这种方案也不好,来看看第三种方案。
需要定义两种结构体
从上图中可以看到,(表头结点的)第一栏包含基本数据信息,第二栏则包含一个指针域。
采用上述方法,
通过每个结点(表头结点)的指针域可以很方便的找到孩子结点。
假设这里寻找结点I的双亲结点,需要怎么做呢?
已知I的结点下标为8,我们就需要通过遍历每个结点的指针域,直到找到指针域所指向的孩子结点中值为8的结点。
这种通过遍历的方式,时间性能还是相对来说比较慢了,那如果我们要求快一点呢?那应该如何对上述结构再进一步优化呢?
增加一列来存储当前结点的双亲结点的下标。
通过这种方式,我们发现可以快速的找到双亲结点和孩子结点,因此,我们就把这种方式称为双亲孩子表示法。
方法二:孩子兄弟表示法
沿着指针指向很容易就可以找到当前结点的兄弟结点。
假设这里寻找结点B的孩子结点,需要怎么做呢?
我们首先找到结点B的 firstChild 指针域找到它的第一个孩子结点,然后再通过第一个孩子结点的 rightSib 指针域找到其他孩子结点,也是非常方便的。