title: 数据结构之B+树
date: 2018-11-04 20:39:00
tags: 数据结构与算法之美


一、 浅谈B-树索引

1.B-树的特性

一棵m阶B-树,或者是空树,或者是满足以下性质的m叉树

解释:
m阶的意思是这颗树最多的分支是多少,每个节点可以包含很多关键字,范围是[[m/2]-1,m-1],比如m为 3,就说明是3阶的B-树,
那么它的树的节点的关键字最少2,最多4个。下面的B+树也是同样的理解。

2.B-树索引结构图

数据结构之B+树-LMLPHP

3.B-树查找代价分析

设关键字的总数为 N ,求 m阶 B- 树的最大层次 L。
数据结构之B+树-LMLPHP

二、 B+树的索引结构

1.B+树特性

在实际的文件系统中,基本上不使用B_树,而是使用B_树的一种变体,称为m阶B+树。 它与B-树的主要不同是叶子结点中存储记录。在B+树中,所有的非叶子结点可以看成是索引,而其中的关键字是作为“分界关键
字”,用来界定某一关键字的记录所在的子树。一棵m阶B+树与m阶B-树的主要差异是:

2.B+树索引结构图

数据结构之B+树-LMLPHP

三、 B+树的索引操作

1.B+树查找

对B+树可以进行两种查找运算:

  1. 从最小关键字开始,进行顺序查找。
  2. 从根结点开始,进行随机查找

在查找时,若非终端结点上的关键值等于给定值,并不终止,而是继续向下直到叶子结点。因此,在B+树中,不管查找成功与否,每次查找都是走了一条从根到叶子结点的路径。其余同B-树的查找类似。

// 在树中查找数据
bool BPlusTree::Search(KEY_TYPE data, char* sPath)
{
    int i = 0;
    int offset = 0;
    if (NULL != sPath)
    {
        (void)sprintf(sPath+offset, "The serach path is:");
        offset+=19;
    }

    CNode * pNode = GetRoot();
    // 循环查找对应的叶子结点
  while (NULL != pNode)
    {
        // 结点为叶子结点,循环结束
        if (NODE_TYPE_LEAF == pNode->GetType())
        {
            break;
        }

        // 找到第一个键值大于等于key的位置
        for (i = 1; (data >= pNode->GetElement(i) )&& (i <= pNode->GetCount()); i++)
        {
        }

        if (NULL != sPath)
        {
            (void)sprintf(sPath+offset, " %3d -->", pNode->GetElement(1));
            offset+=8;
        }

        pNode = pNode->GetPointer(i);
    }
      // 没找到
    if (NULL == pNode)
    {
        return false;
    }
if (NULL != sPath)
    {
        (void)sprintf(sPath+offset, "%3d", pNode->GetElement(1));
        offset+=3;
    }

    // 在叶子结点中继续找
    bool found = false;
    for (i = 1; (i <= pNode->GetCount()); i++)
    {
        if (data == pNode->GetElement(i))
        {
            found = true;
        }
    }

if (NULL != sPath)
    {
        if (true == found)
        {

            (void)sprintf(sPath+offset, " ,successed.");
        }
        else
        {
            (void)sprintf(sPath+offset, " ,failed.");
        }
    }

    return found;
}
       

2.B+树查找代价分析

数据结构之B+树-LMLPHP

3.B+树插入

m阶B树的插入操作在叶子结点上进行,假设要插入关键值a,找到叶子结点后插入a,做如下算法判别:

1、如果当前结点是根结点并且插入后结点关键字数目小于等于m,则算法结束;

2、如果当前结点是非根结点并且插入后结点关键字数目小于等于m,则判断若a是新索引值时转步骤④后结束,若a不是新索引值则直接结束;

3、如果插入后关键字数目大于m(阶数),则结点先分裂成两个结点X和Y,并且他们各自所含的 关键字个数分别为:u=大于(m+1)/2的最小整数,v=小于(m+1)/2的最大整数;由于索引值位于结点的最左端或者最右端,不妨假设索引值位于结点最右端,有如下操作:
a)如果当前分裂成的X和Y结点原来所属的结点是根结点,则从X和Y中取出索引的关键字,将这两个关键字组成新的根结点,并且这个根结点指向X和Y,算法结束;
b)如果当前分裂成的X和Y结点原来所属的结点是非根结点,依据假设条件判断,如果a成为Y的新索引值,则转步骤④得到Y的双亲结点P,如果a不是Y结点的新索引值,则求出X和Y结点的双亲结点P;然后提取X结点中的新索引值a’,在P中插入关键字a’,从P开始,继续进行插入算法;

4、提取结点原来的索引值b,自顶向下,先判断根是否含有b,是则需要先将b替换为a,然后从根结点开始,记录结点地址P,判断P的孩子是否含有索引值b而不含有索引值a,是则先将孩子结点中的b替换为a,然后将P的孩子的地址赋值给P,继续搜索,直到发现P的孩子中已经含有a值时,停止搜索,返回地址P。

4.B+树的插入过程

数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP
数据结构之B+树-LMLPHP

5.B+树的删除操作

B+树的删除仅在叶结点上进行。

当叶子结点中的最大关键字被删除时,其在非终端结点中的值可以作为一个“分界关键字”存在。若因删除而使结点中关键字的个数少于m/2 (m/2结果取上界,如5/2结果为3)时,其和兄弟结点的合并过程亦和B-树类似。

6.B+树的删除过程

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

数据结构之B+树-LMLPHP

四、 B+树的优分析

1.为什么选择B+树?

1、B+树的磁盘读写代价更低

我们都知道磁盘时可以块存储的,也就是同一个磁道上同一盘块中的所有数据都可以一次全部读取。而B+树的内部结点并没有指向关键字具体信息的指针 。因此其内部结点相对B_树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。这样,一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。

2、B+树的查询效率更加稳定。

由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。


11-05 13:57