数据结构之B树

来源:互联网 发布:mac粉 编辑:程序博客网 时间:2024/05/16 07:29


             小目录

           1.前言

           2.B树的查找

           3.B树的查找分析

           4.B树的插入和删除

     

        1.前言

        B树是一种平衡的多路查找树,B-tree算法减少定位记录时所经历的中间过程,从而加快存取速度。普遍运用在数据库和文件系统。关于在数据库中应用可以看看这个链接感觉还可以。

         B树具体定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:
         ⑴树中每个结点至多有m 棵子树;
         ⑵若根结点不是叶子结点,则至少有两棵子树;
         ⑶除根结点之外的所有非终端结点至少有⎡m/2⎤ 棵子树;
         ⑷所有的非终端结点中包含以下信息数据:(n,A0,K1,A1,K2,…,Kn,An其中:Ki(i=1,2,…,n)为关键码,且Ki<Ki+1,Ai 为指向子树根结点的指针(i=0,1,…,n),且指针Ai-1 所指子树中所有结点的关键码均小于Ki (i=1,2,…,n),An 所指子树中所有结点的关键码均大于Kn, ⎡m/2⎤ −1≤n≤m −1 ,n 为关键码的个数。
         ⑸所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。

       如图一棵四阶B-树,其深度为4.

                                     


有定义可知道其相应的数据存储结构如下:

//关键词类型typedef int KeyType;//B树节点结构typedef struct BTNode {int keynum;//树节点中关键词个数struct BTNode *parent;//指向双亲节点//节点向量类型struct Node {KeyType key;//关键词向量struct BTNode *ptr; //子树指向向量KeyType recptr;//记录指针向量}node[m + 1];//注意这里的0号单元中ptr有使用到,其他没有使用到}*BTree;//查找结果结构typedef struct {struct BTNode *pt;//指向找到的节点或是应该插入的位置int i;//在节点中关键词的序号int tag;//1代表成功,0代表查找失败}Result;

    2.B树的查找

    B树的查找类似二叉排序树的查找,所不同的是B树每个结点上是多关键码的有序表,在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码。

    具体的实现算法如下:

/** @description:B树中的查找* @more:若查找成功,则tag == 1,指向节点中和K相等的关键词否则tag == 0,pt指向节点的第i个元素,意味着关键词应该插入在第i和第i+1之间*/Result SearchBTree(BTree T,KeyType key) {BTree p,q;Status found;int i;Result r;//初始化p = T;q = NULL;//q用于指向双亲节点found = 0;i = 0;while(p && !found) {//返回当前节点中等于K或是第一个大于K的关键词的序号i = Search(p,key);//找到关键词if(i > 0 && p->node[i].key == key)found = 1;//在当前节点查找失败else {q = p;p = p->node[i].ptr;//指向下一个待查找节点}}r.i = i;//查找成功if(found) {r.pt = p;r.tag = 1;}//查找失败else {r.pt = q;r.tag = 0;}return r;}

    在上图的B树上查找关键字47的过程如下:

1)首先从更开始,根据根节点指针找到 *节点,因为 *a 节点中只有一个关键字,且给定值47 > 关键字35,则若存在必在指针A1所指的子树内。

2)顺指针找到 *c节点,该节点有两个关键字(43和 78),而43 < 47 < 78,若存在比在指针A1所指的子树中。

3)同样,顺指针找到 *g节点,在该节点找到关键字47,查找成功。


     3.B树的查找分析

   

      B树的查找是由两个基本操作交叉进行的过程,即 ⑴在B树上找结点; ⑵在结点中找关键码。
由于,通常B-树是存储在外存上的,操作⑴就是通过指针在磁盘相对定位,将结点信息读入内存,之后,再对结点中的关键码有序表进行顺序查找或折半查找。因为,在磁盘上读取结点信息比在内存中进行关键码查找耗时多,所以,在磁盘上读取结点信息的次数,即B树的层次树是决定B树查找效率的首要因素。那么,对含有n 个关键码的m 阶B-树,最坏情况下达到多深呢?可按二叉平衡树进行类似分析。首先,讨论m 阶B树各层上的最少结点数。

                       

     4.B树的插入

     在B-树上插入关键码与在二叉排序树上插入结点不同,关键码的插入不是在叶结点上进行的,而是在最底层的某个非终端结点中添加一个关键码,若该结点上关键码个数不超过m-1 个,则可直接插入到该结点上;否则,该结点上关键码个数至少达到m 个,因而使该结点的子树超过了m 棵,这与B-树定义不符。所以要进行调整,即结点的“分裂”。方法为:关键码加入结点后,将结点中的关键码分成三部分,使得前后两部分关键码个数个结点将其插入到父结点中。若插入父结点而使父结点中关键码个数超过m-1,则父结点继续分裂,直到插入某个父结点,其关键码个数小于m。可见,B-树是从底向上生长的。

  比如下列关键码序,建立5 阶B-树,如图9.16。20,54,69,84,71,30,78,25,93,41,7,76,51,66,68,53,3,79,35,12,15,65
         (1)向空树中插入20,得图(a)。
         (2)插入54,69,84,得图(b)。
         (3)插入71,索引项达到5,要分裂成三部分:{20,54},{69}和{71,84},并将69 上升到该结点的父结点中,如图(c)。
         (4)插入30,78,25,93 得图(d)。
         (5)插41 又分裂得图(e)。    

                         


难点主要集中在分割上,具体算法(相关的函数在源码中)如下:

/** @description:在T的节点*q的第i和第i+1个关键词间插入关键词K,倘若引起节点过大,则顺的双亲链进行必要的分裂调整,是T仍然是m阶B树*/Status InsertBTree(BTree *T,KeyType key,BTree q,int i) {BTree ap;Status finished;int s;KeyType rx;rx = key;//初始化ap = NULL;finished = 0;while(q && !finished) {//插入关键词Insert(&q,i,rx,ap);//插入关键词后节点的关键词书目小于m,则插入完成if(q->keynum < m)finished = 1;//节点关键词数目过大,需要进行分裂else {s = (m + 1) / 2;//分割调整split(&q,&ap);//中间节点rx = q->node[s].recptr;//顺着父亲链往上找合适插入的节点q = q->parent;if(q)i = Search(q,key);}}//顺着父亲链往上找没有找到,则树可能为空或是根节点已经分裂,则需要生成新的根节点if(!finished)NewRoot(T,rx,ap);return TRUE;}


总结:跟平衡二叉树一样都是比较难啃的,关键在插入的操作(脑子不够用了)。

最后附上源码地址:GitHub


0 0