树(树、二叉树、满二叉树、完全二叉树、二叉搜索树、二叉平衡树、B-Tree、B+Tree)
1.树
1.概念
树结构简单来说,就是数据元素之间具有层次关系的非线性结构。它是从自然界中的树抽象而来的,有树根、从树根发源类似枝杈的分支关系以及作为分支重点的叶子等。下面我们给出树结构的完整定义:
树(tree)是由n(n ≥0)个节点组成的有限集合。n=0的树称为空树;n >0 的树T的特点如下:
- 有一个特殊的节点称为根节点(root),它只有直接后继节点,没有直接前驱节点。
- 当n ≥1时,除根节点之外的其他节点分为m(m≥0)个互不相交的集合T1,T2,···,Tm,每个集合Tm(1≤i≤m)本身又是一棵结构与树类同的子树。每棵子树的根节点有且仅有一个直接前驱节点,但可以有零或多个直接后继节点。
2.相关术语
- 节点:节点(node)由数据元素(关键字)以及指向子树的地址(指针)构成;
- 孩子节点与双亲节点:若X节点有子树,则子树的根节点称为X的孩子(child)节点,也称子女节点;相应地,X称为其孩子的双亲(parents)节点,也称为父母节点。一棵树中,根没有双亲节点;
- 兄弟节点:同一双亲的孩子节点之间互称兄弟(sibling)节点;
- 祖先节点与后台节点:X节点的祖先(ancestor)是指从根到X所经过的所有节点;X节点的后代(descendant)是指Xj节点的所有孩子节点,以及孩子节点的孩子;
- 节点的度:节点的度(degree)定义为节点节点所拥有子树的棵树。
- 叶子节点与分支节点:叶子(leaf)节点是指节点的度为0的节点,又称为终端节点。除叶子节点之外的其他节点,称为分支节点或非页节点;
- 树的度:树的度是指树种各节点度的最大值;
- 树的深度或高度:树种节点的最大层次数称为树的高度(depth)或高度(height);
- 无序树与有序树:由树的定义可知,若节点的子树T0,T1,…之间没有次序,称为无序树;如果树中节点的子树T0,T1,…从左至右是有次序的(即不能交换),则称该树为有序树(orderd tree).
2.二叉树
1.概念
二叉树(binary tree)是n(n≥0)个节点组成的有限集合,n=0时称为空二叉树;n>0时,二叉树由一个根节点和两棵互不相交的、分别被称为左子树和右子树的子二叉树构成。
二叉树是一种有序(不一定是什么顺序,或者叫做次序、排列等)树,因为二叉树中每个节点的两棵树有左、右之分,即使只有一个子树,也要区分是左子树还是右子树。
如上图所示,如果把他看成是普通的树结构,则图(a)和(b)表示同一棵树;如果看成是二叉树结构,则(a)和(b)表示两棵不同的二叉树。
二叉树的节点最多有两棵树,所以二叉树的度最多为。
2.二叉树的基本形态
二叉树有5钟基本形态,如下图所示:
3.二叉树的性质
- 若根节点的层次为1,则二叉树第i层的节点数目最多为 2 i − 1 2^{i-1} 2i−1(i≥1)个;
- 在深度为k的二叉树中,至多有
2
k
−
1
2^{k-1}
2k−1个节点(k≥0);
- 满二叉树(full binary tree):一个深度k的且具有
2
k
2^{k}
2k -1(k≥0)个节点的二叉树称为满二叉树,即每一层的节点数目都达到最大值。
- 完全二叉树(complete binary tree):一棵具有n个节点深度为k的二叉树,如果它的每一个节点都与深度为k的满二叉树中编号为1~n的节点一一对应,则称这棵二叉树为完全二叉树。
4.完全二叉树的特点
- 满二叉树一定是完全二叉树,而完全二叉树不一定是满二叉树。完成二叉树只有最下面一层可以不满,其上各层都可以看成满二叉树;
- 完全二叉树是具有二叉树结构的而不一定满的二叉树;
- 完全二叉树至多只有最下面两层节点的度可以小于2;
- 完全二叉树最下面一层的节点都集中在该层最左边位置上,如下图就不是完全二叉树
5.二叉树的遍历
遍历二叉树就是按照一定的规则和次序访问二叉树中的所有节点,并且每个节点仅被访问一次。若规定对子树的访问按“先左后右”的次数进行,则遍历二叉树有3种次序:
- 先根次序:访问根节点,遍历左子树,遍历右子树
- 中根次序:遍历左子树,遍历根节点,遍历右子树
- 后根次序:遍历左子树,遍历右子树,遍历根节点
关于二叉树遍历的代码实现,请查看博客二叉树的三种遍历方式(JAVA版)
3.二叉搜索树
1.概念
二叉搜索树,又称二叉排序树(Binary Search Tree,Binary Sort Tree),二叉搜索树或为空树;或者是这样一棵二叉树,若左子树不为空,则左子树上所有节点元素的值均小于根节点,若右子树不为空,则右子树上所有节点元素的值的均大于根节点,其左、右子树也是二叉搜索树。这一规则适用于二叉查找树中的每一个节点。二叉搜索树的时间复杂度为O(log(n))。如下图所示:
2.二叉搜索树操作
关于二叉搜索的构建、遍历及删除等树的操作,请查看上一篇博客《二叉搜索树的构建、遍历及删除(JAVA版)》
4.二叉平衡树(AVL树)
1.概念
平衡二叉树(Balanced Binary Tree)是二叉搜索树的一个进化体,是1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵树,所以它又叫AVL树。
平衡二叉树,指的是若左子树不为空,则左子树上所有节点元素的值均小于根节点,若右子树不为空,则右子树上所有节点元素的值的均大于根节点,这一规则适用于二叉查找树中的每一个节点(其实这就是说二叉平衡树首先它是一棵二叉搜索树),且左子树与右子树的高度差最大为1。
2.为什么要产生二叉平衡树
通过上面的学习我们知道,二叉搜索树就可以让我们的查询变的高效了,不需要从头遍历整棵树上的所有节点,只需要根据当前值与节点值得比对,就可以直接确定下一步的方向,每次比较,都会把搜索范围缩小,类似于二分法。那么既然二叉搜索树的效率已经很高了,为什么要因为二叉平衡树呢?其实这就是二叉搜索树有个缺点,这个缺点我们通过下面的图来看一下:
上面b和竟然变成了链表,同时树的深度也增加了很多,也就是,左、右子树分布极为不平衡。也就是说,插入的序列越接近有序,生成的二叉搜索树就越像是一个链表。为了避免这种情况的发生,即让树的结构看起来尽量“均匀”,左右子树的节点数尽量一样多。
3.二叉平衡树的特点
- 非叶子节点最多拥有两个节点;
- 非叶子节点元素的值
- 树的左右两边的层数相差不会大于1;
5.B树(B-Tree)
1.概念
B树也称B-树,它是一棵多路平衡查找树(查找路径不只两个),它其实是从平衡二叉树变化而来的。B树中所有节点的孩子节点数的最大值称为B树的阶。一棵m阶B树的或为空树,或为满足如下特性的m叉树:
- 树中每个节点最多有m棵子树(即至少有m-1个关键字);–特指非叶子节点(终端节点)
- 若根节点不是终端节点,则至少有两棵子树;
- 除根节点外的所有非叶子节点至少有[m/2](取上界ceiling)棵子树(即最少有[m/2]-1个关键字);
- 非叶节点中的信息包括[n,P0,K1,P1,K2,P2,…,Kn,Pn],其中n表示该节点中保存的关键字个数,K为关键字且Ki<Ki+1,Pi为指向子树根节点的指针。并且P(i-1)所指的子树中所有节点的均小于Ki;
- 所有节点关键字是按递增次序排列,并遵循左小右大原则;–特指非叶子节点(终端节点)
- 叶子节点的的特征:叶子节在相同的层,并且这些节点不带信息(不带指针和关键字),实际上这些节点就表示找不到指定的值,也就是指向这些节点的指针为空。
2.B树的高度
我们假设:n个关键字,阶数为m,高度为h的B树,那么它的高度满足什么的范围呢?
首先,第一层的节点数:1
第二层的节点数:m
第三层的节点数:m*m=
m
2
m^{2}
m2
.
.
第h层的节点数:
m
h
−
1
m^{h-1}
mh−1
而B树中每个节点最多有m-1个关键字,
所以关键字的个数n满足一下条件:
n≤(m-1)(1+m+
m
2
m^{2}
m2+…+
m
h
−
1
m^{h-1}
mh−1) =
m
h
m^{h}
mh -1
计算可得:
l
o
g
m
(
n
+
1
)
log_{m}(n+1)
logm(n+1) ≤h
其次,B树第h层最少的关键字数量满足:
2
(
m
/
2
)
h
(m/2)^{h}
(m/2)h≤n+1
所以我们得出:
l
o
g
m
(
n
+
1
)
log_{m}(n+1)
logm(n+1) ≤h≤
l
o
g
m
/
2
(
(
n
+
1
)
/
2
)
log_{m/2}((n+1)/2)
logm/2((n+1)/2)+1
从上面的公式我们可以看出,m(阶数)对于树的高度有决定性的影响。因此如果每个节点包含更多的元素个数,在元素个数相同的情况下,则更有可能减少B树的高度。
3.B树的查询流程
假如有下面一棵B树,我们要查找24,
- 获取根节点的关键字进行比较,当前根节点关键字为21和35,21<24<35,所以找到指向21和35中间的子节点(二分法规则,左小右大,左边放小于当前节点值的子节点、右边放大于当前节点值的子节点);
- 拿到关键字26和33,24<26 所以直接找到左子节点;
- 拿到23和24,然后遍历,因为24=24 所以直接返回关键字和指针信息(如果树结构里面没有包含所要查找的节点则返回null);
4.B树与二叉平衡树的比较
我们知道,B 树是为了磁盘或其它存储设备而设计的一种多叉平衡查找树,它在降低磁盘I/0操作方面要更好一些,而磁盘的IO非常慢,降低磁盘的IO,那么查找的速度就会明显提高,那与平衡二叉树来比较,它又有怎么样的提高呢?
从上面3中我们查找24来看,只需要进行3次的IO操作就可以查找出24来,如果是平衡二叉树呢?显然树的高度会高很多,IO的次数也会增多。
所以,B树相对于平衡二叉树的不同是,每个节点包含的关键字增多了,特别是在B树应用到数据库中的时候,数据库充分利用了磁盘块的原理(磁盘数据存储是采用块的形式存储的,每个块的大小为4K,每次IO进行数据读取时,同一个磁盘块的数据可以一次性读取出来)把节点大小限制为能充分使用在磁盘块大小范围;把树的节点关键字增多后树的层级比原来的二叉树少了,减少数据查找的次数和复杂度;
6.B+树(B+Tree)
1.概念
B+Tree是由B-Tree改进而来,在这里我们不给出具体的定义了,只说一下它的特点(与B-Tree相比):
一棵m阶的B+树和m阶的B树的差异在于:
- 有n棵子树的结点中含有n个关键字; (而B 树是n棵子树有n-1个关键字);
- 所有的叶子结点中包含了全部关键字的信息(即包含所有的索引值),及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 (而B 树的叶子节点并没有包括全部需要查找的信息);
- 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)
2.B+树的优点(对比B-Tree)
- B+树的层级更少:相较于B-Tree,B+每个非叶子节点不需要存储全部索引,只需要存索引近可以了,所以存储的关键字数更多,树的层级更少,B+树的结构B-Tree更加的“矮胖”,IO次数就更少了,比所以查询数据更快;
- B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同,而B-Tree只要匹配到要查找的元素即可,无论匹配元素的位置处在哪个非叶子节点,所以查询速度要比B-Tree更稳定;
- B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B-Tree高。
- B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B-Tree一样需要对每一层进行遍历,这有利于数据库做全表扫描。
注意:B-Tree其实有时候也并不B+树差,如果经常访问的数据离根节点很近,而B树的非叶子节点本身存有关键字其数据的地址,所以这种数据检索的时候会要比B+树快。
6.总结
到此为止,关于这几种树就介绍完了,里面可能有一些理解不对的地方,欢迎大家指正。