多路平衡查找树B-Tree

多路平衡查找树(下面简称B-Tree)是一棵自平衡树,可以认为是平衡二叉树的泛化版。一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:

  • 根结点至少有两个子女;
  • 每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
  • 除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
  • 所有的叶子结点都位于同一层。

假设有一棵平衡的3路搜索树,他的结构如图

多路平衡查找树B-Tree_第1张图片

上图是一个简单的B-Tree,我们通过图对B-Tree的定义对号入座,可以看到满足如下:

  1. 根结点有两个子女。
  2. 所有非根节点,关键字个数满足┌3/2┐ - 1 <= 2 <= 3 - 1。
  3. 除根结点以外的所有结点(不包括叶子结点)的子节点数正好是关键字总数加1。
  4. 所有的叶子结点都位于同一层。
  5. 对于N个关键字的B-Tree,它的叶子节点数是N+1。17个关键字  + 1 = 18个叶子节点。
  6. B-Tree中,所有关键字以从小到大的顺序排列。
  7. 对于一棵B-Tree,它的查找效率是log┌M/2┐((N+1)/2 )+1。M是路数, N是关键字个数。

B-Tree需要满足定义和保持平衡,所以在进行插入或者删除时,需要作出相应的调整使树满足B-Tree的条件并保持平衡。

插入关键字:

插入关键字主要分为两种情况,假设节点S当前的关键字个数为J <= M-1, 要插入的关键字为K:

  1. 若J < M-1, 则直接在对应位置插入K。
  2. 若J = M-1, 则节点会产生页分裂:此时需要找出S节点原始关键字和K关键字的中位数关键字Kmid,Kmid左右两边分裂成两个节点,并将Kmid节点插入到S节点的父节点对应的位置。父节点递归上述过程。

删除关键字:

删除关键字也分为两种情况,假设当前节点为S,要删除的关键字为K:

  1. 若S是叶子节点,则直接删除,假如发生了下溢(不满足┌m/2┐ - 1 <= j <= m - 1),则需要调整再平衡。
  2. 若S是内部节点,K是其左右两个子节点的分隔符,因此需要选取左节点的最大值或者右节点的最小值替换K节点作为分隔符,若子节点发生了下溢,则需要调整再平衡。

删除调整再平衡:

  • 如果缺少元素节点的右兄弟存在且拥有多余的元素,那么向左旋转
    1. 将父节点的分隔值复制到缺少元素节点的最后(分隔值被移下来;缺少元素的节点现在有最小数量的元素)
    2. 将父节点的分隔值替换为右兄弟的第一个元素(右兄弟失去了一个节点但仍然拥有最小数量的元素)
    3. 树又重新平衡
  • 否则,如果缺少元素节点的左兄弟存在且拥有多余的元素,那么向右旋转
    1. 将父节点的分隔值复制到缺少元素节点的第一个节点(分隔值被移下来;缺少元素的节点现在有最小数量的元素)
    2. 将父节点的分隔值替换为左兄弟的最后一个元素(左兄弟失去了一个节点但仍然拥有最小数量的元素)
    3. 树又重新平衡
  • 否则,如果它的两个直接兄弟节点都只有最小数量的元素,那么将它与一个直接兄弟节点以及父节点中它们的分隔值合并
    1. 将分隔值复制到左边的节点(左边的节点可以是缺少元素的节点或者拥有最小数量元素的兄弟节点)
    2. 将右边节点中所有的元素移动到左边节点(左边节点现在拥有最大数量的元素,右边节点为空)
    3. 将父节点中的分隔值和空的右子树移除(父节点失去了一个元素)
      • 如果父节点是根节点并且没有元素了,那么释放它并且让合并之后的节点成为新的根节点(树的深度减小)
      • 否则,如果父节点的元素数量小于最小值,重新平衡父节点

对于B-Tree的思考:

B-Tree是平衡二叉树的泛化变体,从查找效率上来说,平衡二叉树​是优于B-Tree,但是在一些条件下,B-Tree的执行表现会好很多,例如,目前大多数数据库存储引擎的实现都采用B-Tree或者B+-Tree(B-Tree的变形),而不是直接采用平衡二叉树。在IO密集型的系统中​​​​​​,限制系统运行效率的关键往往是IO的速度,一次IO花费的时间往往是CPU时间的几十上百万倍,因此,在进行数据的读取时,会预读跟数据相邻的数据,通过局部性原理,提高磁盘访问的效率(查询次数/磁盘读取次数)。B-Tree的结构满足了这种场景的需要,虽然查找次数提高了一些,但相比昂贵的磁盘IO,是非常值得的。

你可能感兴趣的:(mysql)