GitHub源码分享
1. 前言
迄今为止,已经介绍了《 二叉查找树 》和《 AVL树 》,我们始终假设可以把整个数据结构存储在内存中。可是,如果数据多到内存装不下,这就意味着必须把数据放在磁盘上,显然这些数据结构不再适用。
问题在于磁盘的I/O速度是远远不如内存访问速度的,然而从一棵树中查找到某个元素,必须从根节点一层层往下找,这每一次查找便是一次I/O操作。为了提高性能,就必须要减少查找的次数。
如能减少树的高度、增加每个节点中的元素数,便是种有效的解决方案。实现这种想法的一种方法是使用B树。
2. 术语
在介绍B树时会用到一些术语,这里先看一下:
根节点(root):没有父节点的节点叫做根节点
叶子节点(leaf):没有子节点的节点叫做叶子节点
内部节点(internal):除根节点和叶子节点之外的节点叫做内部节点。它们即有父节点,也有子节点。
键:B树中的存储元素是键,是用于指向数据记录的指针。键的值是用于存储真正的数据记录。一个节点中可以拥有多个键。
阶:B树的阶为最大子节点数量,其比键的数量大1。我们一般称一个B树为M阶的B树,那么该B树最多拥有M个子节点,节点中最多拥有M-1个键。
更多术语请参阅之前分享的《 树 》!
3. B树(B-Tree)
B树与二叉树(Binary Tree)不是一个概念,你可以将其翻译成Balance Tree,或者是Bayer Tree。
B树是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。
B树与AVL树不同,可以拥有2个以上的子节点,并且每个节点可以有多个键值,这些属性减少了定位记录时所经历的中间过程,加快了存取速度。B树更适用于读写相对较大的数据块存储系统,如磁盘。这种数据结构常被应用在数据库和文件系统的实现上。
对于一个M阶B树具有以下特性:
每个节点最多有 M 个子节点;每个内部节点最少有 ⌈M/2⌉ 个子节点(⌈x⌉为向上取整符号);如果根节点不是叶子节点,那么它至少有两个子节点。
具有 N 个子节点的非叶子节点拥有 N-1 个键。
所有叶子节点必须处于同一层上。
注:如下图是一棵5阶B树的两种画法,最准确的画法应该为图中左B树。但为了方便,通常会将节点中键为空的位置省去不画,如图中右B树。不能因为右图中最多的键为3,就判断这是一棵4阶B树,B树的阶是预先定义好的。
B树的两种画法
4. B树的操作
在对B树进行操作时,可能会违反B树的特性,如最小子节点数、每个节点最小键数。为了维护B树的这些特性,树可能会分裂或合并。
下面我们会以一棵5阶B树来讲述其搜索、插入、删除操作。先来看下5阶B树的特性:
内部节点至少有3个子节点(⌈5/2⌉ = 3),最多有5个子节点
每个节点至少有2个键(3-1=2),最多有4个键
4.1 搜索
B树的搜索和二叉搜索树类似,从根节点开始,从上往下递归的遍历树。在每一层节点上,使用二分查找法匹配目标键,或者通过键的范围来确定子树。
4.2 插入
对于新元素的插入,都是发生在叶子节点上的。所有的插入操作都是从根节点开始,搜索这棵树,并找到该元素应该被插入的节点。将新元素插入到该节点需要如下步骤:
如果该节点上的元素数未满,则将新元素插入到该节点,并保持节点中元素的顺序。
如果该节点上的元素已满,则需要将该节点平均地分裂成两个节点:
从该节点中的元素和新元素先出一个中位数
小于中位数的元素放到左边节点,大于中位数的元素放到右边节点,中位数做为分隔值。
分隔值被插入到父节点中(增加了树的高度),这可能会导致父节点的分裂,分裂父节点时又可能会使它的父节点分裂,以此类推。如果分裂一直上升到根节点,那么就创建一个新的根节点,它有一个分隔值和两个子节点。(这就是根节点并不像内部节点一样有最少子节点数量限制的原因)
下图是一个5阶B树,我们通过顺序插入1到17,来观察节点的分裂过程。
B树的插入
4.3 删除
B树的删除就复杂了许多,可分为下面几种情况:
删除叶子节点中的元素
(1)搜索要删除的元素
(2)如果它在叶子节点上,直接将其删除
(3)如果删除后产生了下溢出(键数小于最小值),则向其兄弟节点借元素。即将其父节点元素下移至当前节点,将兄弟节点中元素上移至父节点(若是左节点,上移最大元素;若是右节点,上移最小元素)
(4)若兄弟节点也达到下限,则合并兄弟节点与分割键。
删除内部节点中的元素
(1)内部节点中元素为其左右子节点的分割值,需要从左子节点最大元素或右子节点最小元素中选出一个新的分割符。被选中的分割符从原子节点中移除,作为新的分隔值替换掉被删除的元素。
(2)上一步中,若左右子节点元素数均达到下限,则合并左右子节点。
(3)若删除元素后,其中节点元素数小于下限,则继续合并。
下图是一个5阶B树,我们通过删除15、14、17、5四个键,来观察删除过程(基本涵盖所有情况)。
B树的删除
5. 代码实现
本代码实现了B树的搜索、插入、删除操作,下面看详细介绍。
5.1 键值类
该类用于存放B树每个节点中的键值数据。
key:节点中的键,用于指向数据记录。
value:键向指向的数据记录。
由于键在节点中是顺序存储的,所以实现了Comparable接口
class Entry implements Comparable { </