欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

B树第一节学习

程序员文章站 2022-07-15 08:53:35
...

具体讲解之前,有一点,再次强调下: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时要检查的结点)如图:

 


B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
 

 

    相信,从上图你能轻易的看到,一个内结点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;

 

 

 

 

 

节点定义如下图所示:


B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
 

 

     为了简单,这里用少量数据构造一棵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树的高度公式:  
B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 

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

 

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

 

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

           B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 

 

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

 

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

 

  • B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
  • 大小: 28.4 KB
  • B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
  • 大小: 36.3 KB
  • B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
  • 大小: 3 KB
  • B树第一节学习
            
    
    博客分类: B树  算法  数据结构 B树  算法  数据结构 
  • 大小: 1.2 KB