前言
之前讲到的树,它本身只能够存储一个元素,如果在元素非常多的时候,树的孩子和树的高度就会很大,读取的过程中势必会消耗更多的时间。为了避免这一现象,我们引入了同一个节点中可以存储多个元素的多路查找树。
多路查找树不同于二叉树,二叉树只能由两个孩子,而多路查找树可以有多个孩子。例如2-3树的每个节点可以有两个或者三个孩子,2-3-4树
- 2-3树
2-3树是一棵多路查找树,每一个节点都具有两个孩子(称为2节点)或者三个孩子(称为3节点)。
2-3树的所有叶子都需要在同一层次上。
2节点:包含一个元素与两个孩子(或者没有孩子),注意2节点要么有两个孩子,要么没有孩子,不能只有一个孩子。
3节点:包含一大一小两个元素和三个孩子(或者没有孩子),注意3节点要么有三个孩子,要么没有孩子,不能只有一个或者两个孩子,另外这三个孩子根据大小排列在一大一小的两个元素之间。
-
2-3树样例
-
2-3-4树
理解了2-3树之后,再来看2-3-4树也就很好理解了。2-3-4树相比于2-3树多了一种新的节点:4节点。
4节点:包含小,中,大三个元素以及4个孩子(或者没有孩子),注意4节点要么有4个孩子,要么没有孩子。如果有4个孩子,这四个孩子根据大小排列在4个孩子之间。
由于2-3-4树在编程语言上实现起来较为困难,我们通常用红黑树来代替它。这并不意味着2-3-4树就没有价值了,
2-3-4树的插入与删除和红黑树颜色的翻转和旋转等价,这也是理解红黑树背后逻辑的重要工具。
- 2-3-4树样例
上面讲到的2-3树和2-3-4树都是B树的特例,2-3树是3阶b树,2-3-4树是4阶b树。
B树
B树类似于平衡二叉树,不同的是它的每一个节点可以存储多个值,并且可以拥有多个子节点。
- B树的原理
在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况。为什么会出现这样的情况,我们知道要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写。磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读写。根据磁盘查找存取的次数往往由树的高度所决定,所以,只要我们通过某种较好的树结构减少树的结构尽量减少树的高度,B树可以有多个子女,从几十到上千,可以降低树的高度。
于是B树便出现了。根据B树节点值有序的特点,每一次只需要将单一节点读入内存,用二分法查找关键值,找到的话返回,没有找到的话则确定区间,将下一个节点从磁盘读入内存继续进行查找即可。
这样的设计较好的减小了内外存的数据交互次数,也就较好的减小了时间成本,这就是B树存在的意义。
- B树的特点
- 节点中关键字按从小到大的顺序排列,且对于父节点关键字来说,它的左子树的所有关键字都小于父节点关键字,它的右子树所有关键字都大于父节点的关键字。
- n阶b树最大子节点树为n,根节点关键字个数在 [1,n-1] 之间,非根节点关键字个数在 [n/2,n-1] 之间。
- 所有叶子节点都处于同一层。
- 举例
假设我们查找数字7,首先从外存(如硬盘)中读取3,5,8三个元素,发现7并不在其中,接着再通过外存读取6,7进行查找即可 。
B+树
B+树相当于是B树改进,与B树的不同之处在于:
- 所有的数据都存储在叶子节点,非叶子结点只是作为索引,并不存储data。
- 所有的叶子节点都有一个后继指针指向下一个叶子节点。
B+树与B树的区别
- 相同点
- 最大子节点树为n,根节点关键字个数在 [1,n-1] 之间,非根节点关键字个数在 [n/2,n-1] 之间。
- 不同点
-
B+树由于真正的数据都存放在叶子节点中,所以查找的时间复杂度恒为 O(logN);而B树的查询时间并不固定,可能会为O(1),也可能是O(logN)。
-
B+树在叶子节点中添加了后继指针,遍历所有关键字较为方便,比较适合带范围的查找;B树遍历整个关键字需要通过中序遍历,较为麻烦,无法进行带范围的查找。
-
B+树的非叶子节点并不存储data,同样的一次磁盘IO操作,相比于B树,B+树可以读取更多的节点 从而获取 更多的索引,那么也就可以进行更少的磁盘IO。