注意:首先需要说明的一点是:B - 树就是 B ~树,没有所谓的B 减 树
二叉查找树的查找的时间复杂度是O(log N),其查找效率已经足够高了,
- 那为什么还有B~树和B+树的出现呢?
- 难道它两的时间复杂度比二叉查找树还小吗?
答案当然不是,原因是因为磁盘IO;
- 众所周知,IO操作的效率很低,那么,当在大量数据存储中,查询时我们不能一下子将所有数据加载到内存中,只能逐一加载磁盘页,每个磁盘页对应树的节点。
- 造成大量磁盘IO操作(最坏情况下为树的高度)。
- 平衡二叉树由于树深度过大而造成磁盘IO读写过于频繁,进而导致效率低下。
- 所以,我们为了减少磁盘IO的次数,就你必须降低树的深度,将“瘦高”的树变得“矮胖”。一个基本的想法就是:
(1)每个节点存储多个元素
(2)摒弃二叉树结构,采用多叉树
这样就引出来了一个新的查找树结构 ——多路查找树。
- 根据AVL给我们的启发,一颗平衡多路查找树(B~树)自然可以使得数据的查找效率保证在O(logN)这样的对数级别上。
下面来具体介绍一下B ~树(Balance Tree)
B~ 树
- B~ 树又叫平衡多路查找树
- 一个m阶的B~树具有如下几个特征:B~树中所有结点的孩子结点最大值称为B~树的阶,通常用m表示。
B~树的卫星数据:
示例:三阶B~树(实际中节点中元素很多)
查询
以上图为例:若查询的数值为5:
第一次磁盘IO:在内存中定位(与17、35比较),比17小,左子树;
第二次磁盘IO:在内存中定位(与8、12比较),比8小,左子树;
第三次磁盘IO:在内存中定位(与3、5比较),找到5,终止。
- 整个过程中,我们可以看出:比较的次数并不比二叉查找树少,尤其适当某一节点中的数据很多时,但是磁盘IO的次数却是大大减少。
- 比较是在内存中进行的,相比于磁盘IO的速度,比较的耗时几乎可以忽略。
- 所以当树的高度足够低的话,就可以极大的提高效率。
- 相比之下,节点中的元素多点也没关系,仅仅是多了几次内存交互而已,只要不超过磁盘页的大小即可。
删除和添加操作,自带平衡,B~树是一个自平衡的树
比如,删除11
平衡为:
注意
- ①、B~树主要用于文件系统以及部分数据库索引,例如: MongoDB。
- 而大部分关系数据库则使用 B+树做索引,例如:mysql数据库;
- ②、从查找效率考虑一般要求B~树的阶数m >= 3;
- ③、B~树上算法的执行时间主要由读、写磁盘的次数来决定,故一次I/O操作应读写尽可能多的信息。
- 因此 B~树的结点规模一般以一个磁盘页为单位。
- 一个结点包含的关键字及其孩子个数取决于磁盘页的大小。
B+ 树
- B+树是B~树的变种,有着比B~树更高的查询效率。
- 有k个子树的中间节点包含有k个元素(B~树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据 都保存在叶子节点。
- 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小 自小而大顺序链接。
- 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
- B+树通常有两个指针,一个指向根结点,另一个指向关键字最小的叶子结点。
需要补充的是,
- 在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。
- 在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。
查找
B+树的优势在于查找效率上,下面我们做一具体说明:
- B+树中间节点没有卫星数据(索引元素所指向的数据记录),只有索引,而B~树每个结点中的每个关键字都有卫星数据;
- 这就意味着同样的大小的磁盘页可以容纳更多节点元素,在相同的数据量下,B+树更加“矮胖”,IO操作更少
- 因为卫星数据的不同,导致查询过程也不同;
- B~树的查找只需找到匹配元素即可,最好情况下查找到根节点,最坏情况下查找到叶子结点,所说性能很不稳定,
- 而B+树每次必须查找到叶子结点,性能稳定
- 原因是:B~树在内存中找到的可能是中间节点,需要继续遍历(io操作),
- B+树在内存中匹配到数据节点
插入删除和B~树类似
B+树的卫星数据:
3阶的B+树:
B+树相比B~树的优势:
1.单一节点存储更多的元素,使得查询的IO次数更少;
2.所有查询都要查找到叶子节点,查询性能稳定;
3.所有叶子节点形成有序链表,便于范围查询。