B树第一节学习

具体讲解之前,有一点,再次强调下:B-树,即为B树。因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树,其实,这是个非常不好的直译,很容易让人产生误解。如人们可能会以为B-树是一种树,而B树又是一种一种树。而事实上是,B-tree就是指的B。特此说明。

 

    我们知道,B 树是为了磁盘或其它存储设备而设计的一种多叉(下面你会看到,相对于二叉,B树每个内结点有多个分支,即多叉)平衡查找树。与本blog之前介绍的红黑树很相似,但在降低磁盘I/0操作方面要更好一些。许多数据库系统都一般使用B树或者B树的各种变形结构,如下文即将要介绍的B+树,B*树来存储信息。

 

        B树与红黑树最大的不同在于,B树的结点可以有许多子女,从几个到几千个。那为什么又说B树与红黑树很相似呢?因为与红黑树一样,一棵含n个结点的B树的高度也为Olgn),但可能比一棵红黑树的高度小许多,应为它的分支因子比较大。所以,B树可以在Ologn)时间内,实现各种如插入(insert),删除(delete)等动态集合操作。

 

     如下图所示,即是一棵B树,一棵关键字为英语中辅音字母的B树,现在要从树种查找字母R(包含n[x]个关键字的内结点xxn[x]+1]个子女(也就是说,一个内结点x若含有n[x]个关键字,那么x将含有n[x]+1个子女)。所有的叶结点都处于相同的深度,带阴影的结点为查找字母R时要检查的结点)如图:

 



 

 

    相信,从上图你能轻易的看到,一个内结点x若含有n[x]个关键字,那么x将含有n[x]+1个子女。如含有2个关键字D H的内结点有3个子女,而含有3个关键字Q T X的内结点有4个子女。

 

树又叫平衡多路查找树。一棵m阶的 (m叉树)的特性如下

 

        1.    树中每个结点最多含有m个孩子(m>=2);

 

        2.    除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]个孩子(其中ceil(x)是一个取上限的函数);

 

        3.    若根结点不是叶子结点,则至少有2个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点)。

 

        4.    所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部接点或查询失败的接点,实际上这些结点不存在,指向这些结点的指针都为null);(读者反馈@冷岳:这里有错,叶子节点只是没有孩子和指向孩子的指针,这些节点也存在,也有元素。@JULY:其实,关键是把什么当做叶子结点,因为如红黑树中,每一个NULL指针即当做叶子结点,只是没画出来而已)。

 

         5.    每个非终端结点中包含有n个关键字信息: (nP0K1P1K2P2......KnPn)。其中:


        a)   Ki (i=1...n)为关键字,且关键字按顺序升序排序K(i-1)< Ki 

 

    b)Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1) 

 

       c)   关键字的个数n必须满足: [ceil(m / 2)-1]<= n <= m-1

 

一颗m阶的B树满足的条件

 

   1)每个结点至多有m个子结点;
     
2)除根结点和叶结点外,其它每个结点至少有[m/2] 个子结点;
     
3)若根结点不是叶子结点,则至少有两个子结点;
     
4)所有的叶结点在同一层;
     
5)有k个子结点的非根结点恰好包含k-1个关键码。

 

 

       针对上面第5点,再阐述下:B树中每一个结点能包含的关键字(如之前上面的D HQ T X)数有一个上界和下界。这个下界可以用一个称作B树的最小度数(算法导论中文版上译作度数,最小度数即内节点中节点最小孩子数目tt>=2)表示。

 

    每个非根的结点必须至少含有t-1个关键字。每个非根的内结点至少有t个子女。如果树是非空的,则根结点至少包含一个关键字;

 

每个结点可包含之多2t-1个关键字。所以一个内结点至多可有2t个子女。如果一个结点恰好有2t-1个关键字,我们就说这个结点是满的(而稍后介绍的B*树作为B树的一种常用变形,B*树中要求每个内结点至少为2/3满,而不是像这里的B树所要求的至少半满);

 

 当关键字数t=2t=2的意思是,tmin=2t可以>=2)时的B树是最简单的有很多人会因此误认为B树就是二叉查找树,但二叉查找树就是二叉查找树,B树就是B树,B树的真正最准确的定义为:一棵含有tt>=2)个关键字的平衡多路查找树。每个内结点可能因此而含有2个、3个或4个子女,亦即一棵2-3-4树,然而在实际中,通常采用大得多的t值。

 

        B树中的每个结点根据实际情况可以包含大量的关键字信息和分支(当然是不能超过磁盘块的大小,根据磁盘驱动(disk drives)的不同,一般块的大小在1k~4k左右);这样树的深度降低了,这就意味着查找一个元素只要很少结点从外存磁盘中读入内存,很快访问到要查找的数据。

 

B树的类型

#define m 1024
struct BTNode;
typedef struct BTNode *PBTNode;
struct BTNode {
    int keyNum;   //实际关键字个数,keyNum<m
    PBTNode  parent;   //指向父节点
    PBTNode  *ptr;  //子树指针向量:ptr

[0]...ptr[keyNum]
    KeyType  *key;  //关键字向量:key[0]...key

[keyNum]
}
typedef struct PBTNode  *BTree;
typedef  BTree *PBTree;

 

 

 

 

节点定义如下图所示:



 

 

     为了简单,这里用少量数据构造一棵3叉树的形式,实际应用中的B树结点中关键字很多的。上面的图中比如根结点,其中17比表示一个磁盘文件的文件名;小红方块表示这个17文件内容在硬盘中的存储位置;p1表示指向17左子树的指针。

 

其结构可以简单定义为:

typedef struct {

    /*文件数*/

    int  file_num;

    /*文件名(key)*/

    char * file_name[max_file_num];

    /*指向子节点的指针*/

     BTNode * BTptr[max_file_num+1];

     /*文件在硬盘中的存储位置*/

     FILE_HARD_ADDR offset[max_file_num];

}BTNode;

 

     假如每个盘块可以正好存放一个B树的结点(正好存放2个文件名)。那么一个BTNODE结点就代表一个盘块,而子树指针就是存放另外一个盘块的地址。

 

下面,咱们来模拟下查找文件29的过程:

 

1.    根据根结点指针找到文件目录的根磁盘块1,将其中的信息导入内存。【磁盘IO操作 1次】 

   

2.    此时内存中有两个文件名1735和三个存储其他磁盘页面地址的数据。根据算法我们发现17<29<35,因此我们找到指针p2

 

3.    根据p2指针,我们定位到磁盘块3,并将其中的信息导入内存。【磁盘IO操作 2次】   

 

4.    此时内存中有两个文件名2630和三个存储其他磁盘页面地址的数据。根据算法我们发,26<29<30,因此我们找到指针p2

 

5.    根据p2指针,我们定位到磁盘块8,并将其中的信息导入内存。【磁盘IO操作 3次】    

 

6.    此时内存中有两个文件名2829。根据算法我们查找到文,29,并定位了该文件内存的磁盘地址。

 

     分析上面的过程,发现需要3次磁盘IO操作和3次内存查找操作。关于内存中的文件名查找,由于是一个有序表结构,可以利用折半查找提高效率。至于IO操作时影响整个B树查找效率的决定因素。

 

     当然,如果我们使用平衡二叉树的磁盘存储结构来进行查找,磁盘4次,最多5次,而且文件越多,B树比平衡二叉树所用的磁盘IO操作次数将越少,效率也越高。

 B树的高度

 

    根据上面的例子我们可以看出,对于辅存做IO读的次数取决于B树的高度。而B树的高度由什么决定的呢?

     根据B树的高度公式:  

      其中T为度数(每个节点包含的元素个数),即所谓的阶数,N为总元素个数或总关键字数。

 

     我们可以看出T对于树的高度有决定性的影响。因此如果每个节点包含更多的元素个数,在元素个数相同的情况下,则更有可能减少B树的高度。这也是为什么SQL Server中需要尽量以窄键建立聚集索引。因为SQL Server中每个节点的大小为8092字节,如果减少键的大小,则可以容纳更多的元素,从而减少了B树的高度,提升了查询的性能。

 

    上面B树高度的公式也可以进行推导得出,将每一层级的的元素个数加起来,比如度为T的节点,根为1个节点,第二层至少为2个节点,第三层至少为2t个节点,第四层至少为2t*t个节点。将所有最小节点相加,从而得到节点个数N的公式:

           

 

    两边取对数,则可以得到树的高度公式。

 

    这也就是说每个节点必须至少有两个子元素,因为根据高度公式,如果每个节点只有一个元素,也就是T=1的话,那么高度将会趋于正无穷。

 

猜你喜欢

转载自luozhong915127.iteye.com/blog/1624387