残局

不过在等杀死希望的最后一刀

0%

B树

AVL 树和红黑树都假设所有的数据放在主存当中。而当数据量达到亿级,主存中根本存储不下,我们只能以块的形式从磁盘读取数据,与主存的访问时间相比,磁盘的 I/O 操作相当耗时,而提出 B-树的主要目的就是减少磁盘的 I/O 操作。

B树的一个节点x如果包含nkey,则其有n+1个孩子。(n个key将其余数据划分为n+1个区间,每个孩子介于相应的区间中)

性质

B树所具有的的性质人如下:

  1. 每个节点x应具有如下属性:
    1. x.n即,存储在x节点的key个数
    2. 节点x的n个key以非降序排列。x.key1x.key2x.keyx.n
    3. x.leaf,布尔值。用来表示x是(true)否(false)为叶子节点
  2. 每个非叶子节点还包括x.n+1个指向其孩子的指针:x.c1,x.c2,x.c3,x.cx.n+1叶子节点没有该定义
  3. x.keyi对其子节点进行分割。假设ki为其子节点的key值。则有

k1x.key1k2x.key2x.keyx.nkx.n+1
4. 所有的叶子节点深度相同
5. B树的最小度数t2以限制每个节点所包含key值的个数(上下限)。
1. 除根节点外,每个节点必须至少t1key。(除叶子节点外,每个节点至少有t个孩子)
2. 每个节点至多有2t1个key。(除叶子节点外,每个节点至多有2t个孩子)当B树恰好拥有2t1key时,称该节点为满的。(结合性质5.1即B树要求半满)

一个B树的样例如下:
B树样例
一个较为简单的B树定义

1
2
3
4
5
6
7
8
class BTreeNode 
{
int *keys; // 存储key值
int t; // 最小度
BTreeNode **C; // 子节点
int n; // key的个数
bool leaf; // 是否为叶子节点
};

B树的高度
B树大部分操作的磁盘存取次数与B树的高度成正比。对于任意一个包含nkey,高度为h,最小度为t的B树,有

hlogtn+12

基本操作

在此假定:B树始终在主存中,无需读取磁盘。

搜索

B树的搜索,不过是在二叉搜索树的基础上,每个节点有多个key值。
以上述的样例图中查找33为例。

  1. 首先,访问根节点。先和第一个key值20比较,发现 37>20。然后与第二个key值40比较,此时37<40。于是递归到20与40之间的子节点去查找。
    B树搜索样例1_1
  2. 接着,访问[24,35]节点。先和第一个key 24比较,发现37>24。然后与第二个key 35比较,发现37>35。访问该节点的最后一个子节点。
    B树搜索样例1_2
  3. 最后在叶子节点[37,39]中,查找到37,结束。
    B树查找样例1_3
    相关过程代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    BTreeNode *BTreeNode::search(int k) 
    {
    // 找到第一个大于待查找k 的key值
    int i = 0;
    while (i < n && k > keys[i]) {
    i++;
    }
    // 相等即找到
    if (keys[i] == k)
    return this;

    // 如果没有找到,且当前结点为叶子结点则无解
    if (leaf == true)
    return nullptr;

    // 递归访问恰当的子代
    return C[i]->search(k);
    }
    插入、删除。过于复杂,先略了,有空再写。
-------------感谢阅读有缘再见-------------