当前位置:   article > 正文

B-树的插入、查找、删除 及 可执行的C语言代码_写出b-树的构建,查找,插入和删除代码,并设计一个主函数来呈现该操作。用c语言

写出b-树的构建,查找,插入和删除代码,并设计一个主函数来呈现该操作。用c语言

 前面讨论的查找都是内查询算法,被查询的数据都在内存。当查询的数据放在外存,用平衡二叉树作磁盘文件的索引组织时,若以结点为内外存交换的单位,则找到需要的关键字之前,平均要进行lgn次磁盘读操作,而磁盘、光盘的读写时间要比随机存取的内存代价大得多。其二,外存的存取是以“页”为单位的,一页的大小通常是1024字节或2048字节。

 针对上述特点,1972R.BayerE.M.Cright提出了一种B-树的多路平衡查找树,以适合磁盘等直接存取设备上组织动态查找表B-树上算法的执行时间主要由读、写磁盘的次数来决定,故一次I/O操作应读写尽可能多的信息。因此B-树的结点规模一般以一个磁盘页为单位。一个结点包含的关键字及其孩子个数取决于磁盘页的大小。

一、基本概念

B-树又称为多路平衡查找树。

         一棵度为mB-树称为mB_树。一个结点有k个孩子时,必有k-1个关键字才能将子树中所有关键字划分为k个子集。B-树中所有结点的孩子结点最大值称为B-树的阶,通常用m表示。从查找效率考虑,一般要求m3。一棵m阶的B-树或者是一棵空树,或者是满足下列要求的m叉树:

1)根结点或者为叶子,或者至少有两棵子树,至多有m棵子树。

2)除根结点外,所有非终端结点至少有ceil(m/2)棵子树,至多有m棵子树。

3)所有叶子结点都在树的同一层上。

4)每个结点的结构为:

       nA0K1A1K2A2,…  KnAn

其中,Ki(1in)为关键字,且Ki<Ki+1(1in-1)

        Ai(0in)为指向子树根结点的指针。且Ai所指子树所有结点中的关键字均小于Ki+1An所指子树中所有结点的关键字均大于Kn

n为结点中关键字的个数,满足ceil(m/2)-1nm-1

        比如,一棵3B-树,m=3。它满足: 

1)每个结点的孩子个数小于等于3 

2)除根结点外,其他结点至少有=2个孩子。 

3根结点有两个孩子结点 

4)除根结点外的所有结点的n大于等于=1,小于等于2 

5)所有叶结点都在同一层上。
  
29、B-树及其查找 - 墨涵 - 墨涵天地

二、B-树查找的算法思想

1B-树的查找

B-树的查找过程:根据给定值查找结点和在结点的关键字中进行查找交叉进行。首先从根结点开始重复如下过程:

       若比结点的第一个关键字小,则查找在该结点第一个指针指向的结点进行;若等于结点中某个关键字,则查找成功;若在两个关键字之间,则查找在它们之间的指针指向的结点进行;若比该结点所有关键字大,则查找在该结点最后一个指针指向的结点进行;若查找已经到达某个叶结点,则说明给定值对应的数据记录不存在,查找失败。

2.  B-树的插入

插入的过程分两步完成:

   1)利用前述的B-树的查找算法查找关键字的插入位置。若找到,则说明该关键字已经存在,直接返回。否则查找操作必失败于某个最低层的非终端结点上。

   2)判断该结点是否还有空位置。即判断该结点的关键字总数是否满足n<=m-1。若满足,则说明该结点还有空位置,直接把关键字k插入到该结点的合适位置上。若不满足,说明该结点己没有空位置,需要把结点分裂成两个。

分裂的方法是:生成一新结点。把原结点上的关键字和k按升序排序后,从中间位置把关键字(不包括中间位置的关键字)分成两部分。左部分所含关键字放在旧结点中,右部分所含关键字放在新结点中,中间位置的关键字连同新结点的存储位置插入到父结点中。如果父结点的关键字个数也超过(m-1),则要再分裂,再往上插。直至这个过程传到根结点为止。

29、B-树及其查找 - 墨涵 - 墨涵天地
29、B-树的插入、查找、删除 - EdwardLewis - 墨涵天地

29、B-树及其查找 - 墨涵 - 墨涵天地

29、B-树及其查找 - 墨涵 - 墨涵天地

3B-树的删除

B-树上删除关键字k的过程分两步完成:

   1)利用前述的B-树的查找算法找出该关键字所在的结点。然后根据 k所在结点是否为叶子结点有不同的处理方法。

   2)若该结点为非叶结点,且被删关键字为该结点中第i个关键字key[i],则可从指针son[i]所指的子树中找出最小关键字Y,代替key[i]的位置,然后在叶结点中删去Y

因此,把在非叶结点删除关键字k的问题就变成了删除叶子结点中的关键字的问题了。

B-树叶结点上删除一个关键字的方法是

首先将要删除的关键字 k直接从该叶子结点中删除。然后根据不同情况分别作相应的处理,共有三种可能情况:

1)如果被删关键字所在结点的原关键字个数n>=ceil(m/2),说明删去该关键字后该结点仍满足B-树的定义。这种情况最为简单,只需从该结点中直接删去关键字即可。

2)如果被删关键字所在结点的关键字个数n等于ceil(m/2)-1,说明删去该关键字后该结点将不满足B-树的定义,需要调整。

调整过程为:如果其左右兄弟结点中有“多余”的关键字,即与该结点相邻的右(左)兄弟结点中的关键字数目大于ceil(m/2)-1。则可将右(左)兄弟结点中最小(大)关键字上移至双亲结点。而将双亲结点中小(大)于该上移关键字的关键字下移至被删关键字所在结点中。

3)如果左右兄弟结点中没有“多余”的关键字,即与该结点相邻的右(左)兄弟结点中的关键字数目均等于ceil(m/2)-1。这种情况比较复杂。需把要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的关键字合并成一个结点,即在删除关键字后,该结点中剩余的关键字加指针,加上双亲结点中的关键字Ki一起,合并到Ai(是双亲结点指向该删除关键字结点的左(右)兄弟结点的指针)所指的兄弟结点中去。如果因此使双亲结点中关键字个数小于ceil(m/2)-1,则对此双亲结点做同样处理。以致于可能直到对根结点做这样的处理而使整个树减少一层。

总之,设所删关键字为非终端结点中的Ki,则可以指针Ai所指子树中的最小关键字Y代替Ki,然后在相应结点中删除Y。对任意关键字的删除都可以转化为对最下层关键字的删除。

29、B-树及其查找 - 墨涵 - 墨涵天地

如图示:

a被删关键字Ki所在结点的关键字数目不小于ceil(m/2),则只需从结点中删除Ki和相应指针Ai,树的其它部分不变。

29、B-树及其查找 - 墨涵 - 墨涵天地

b、被删关键字Ki所在结点的关键字数目等于ceil(m/2)-1,则需调整。调整过程如上面所述。

29、B-树及其查找 - 墨涵 - 墨涵天地

c、被删关键字Ki所在结点和其相邻兄弟结点中的的关键字数目均等于ceil(m/2)-1,假设该结点有右兄弟,且其右兄弟结点地址由其双亲结点指针Ai所指。则在删除关键字之后,它所在结点的剩余关键字和指针,加上双亲结点中的关键字Ki一起,合并到Ai所指兄弟结点中(若无右兄弟,则合并到左兄弟结点中)。如果因此使双亲结点中的关键字数目少于ceil(m/2)-1,则依次类推。

29、B-树及其查找 - 墨涵 - 墨涵天地

29、B-树及其查找 - 墨涵 - 墨涵天地

三、B-树的C语言描述

1、存储结构

29、B-树及其查找 - 墨涵 - 墨涵天地

29、B-树及其查找 - 墨涵 - 墨涵天地

2、插入

29、B-树及其查找 - 墨涵 - 墨涵天地

3、查找

29、B-树及其查找 - 墨涵 - 墨涵天地

四、B-树的C语言实现

#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#define OK 1
#define ERROR -1
#define m 3 //3阶树
#define N 16 //数据元素个数
#define MAX 5 //字符串最大长度+1
typedef int KeyType;
struct Others  //记录的其它部分
{
char info[MAX];
};
struct Record
{
KeyType key; //关键字
Others others; //其它部分
};
typedef struct BTNode
{
int keynum; //结点中关键字个数
BTNode *parent;//指向双亲节点
   struct Node  //结点向量类型
   {
   KeyType key; //关键字向量
   BTNode *ptr;//子树指针向量
   Record *recptr; //记录向量指针
   }node[m+1]; //key,recptr的0号单元未用
}BTNode,*BTree;
struct Result //B树的查找结果类型
{
BTNode *pt; //指向找到的结点
int i; //在节点中关键字序号,1...m
int tag; //1表示查找成功,0表示查找失败。
};

int InitDSTable(BTree &DT)
{
DT=NULL;
return OK;
}//InitDSTable

void DestroyDSTable(BTree &DT)
{
int i;
if(DT) //非空树
    {
     for(i=0;i<=DT->keynum;i++)
         DestroyDSTable(DT->node[i].ptr);
     free(DT);
     DT=NULL;
    }//if
}//DestroyDSTable

int Search(BTree p,KeyType K)
{//在p->node[1...keytype].key中查找i,使得p->node[i].key<=K<
    //p->node[i+1].key
    int i=0,j;
    for(j=1;j<=p->keynum;j++)
        if(p->node[j].key<=K)
            i=j;
    return i;
}//Search

void Insert(BTree &q,int i,Record *r,BTree ap)
{//将r->key、r和ap分别插入到q->key[i+1]、
    //q->recptr[              i+1]和q->ptr[i+1]中
    int j;
    for(j=q->keynum;j>i;j--) //空出q->node[i+1]
     q->node[j+1]=q->node[j];
    q->node[i+1].key=r->key;
    q->node[i+1].ptr=ap; //前加入的结点,还没有儿子结点
    q->node[i+1].recptr=r;
    q->keynum++;
}//Insert

void NewRoot(BTree &T,Record *r,BTree ap)
{// 生成含信息(T,r,ap)的新的根结点*T,原T和ap为子树指针
BTree p;
p=(BTree)malloc(sizeof(BTNode));
p->node[0].ptr=T;
T=p;
if(T->node[0].ptr)
    T->node[0].ptr->parent=T;
T->parent=NULL;
T->keynum=1;
T->node[1].key=r->key;
T->node[1].recptr=r;
T->node[1].ptr=ap;
if(T->node[1].ptr)
    T->node[1].ptr->parent=T;
}//NewRoot

void split(BTree &q,BTree &ap)
{// 将结点q分裂成两个结点,前一半保留,后一半移入新生结点ap
int i,s=(m+1)/2;
ap=(BTree)malloc(sizeof(BTNode));//生成新结点ap
ap->node[0].ptr=q->node[s].ptr;//原来结点中间位置关键字相应指针指向的子树放到
                               //新生成结点的0棵子树中去
for(i=s+1;i<=m;i++) //后一半移入ap
   {
   ap->node[i-s]=q->node[i];
   if(ap->node[i-s].ptr)
       ap->node[i-s].ptr->parent=ap;
   }//for
   ap->keynum=m-s;
   ap->parent=q->parent;
   q->keynum=s-1; // q的前一半保留,修改keynum
}//split

void InsertBTree(BTree &T,Record *r,BTree q,int i)
{//在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K的指针r。若引起
   // 结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。
BTree ap=NULL;
int finished=false;
int s;
Record *rx;
rx=r;
while(q&&!finished)
   {
    Insert(q,i,rx,ap);//将r->key、r和ap分别插入到q->key[i+1]、
                      //q->recptr[i+1]和q->ptr[i+1]中
    if(q->keynum<m)
        finished=true;
    else
      {//分裂结点*q
      s=(m+1)/2;
      rx=q->node[s].recptr;
      split(q,ap);//将q->key[s+1..m],q->ptr[s..m]和q->recptr[s+1..m]
                  //移入新结点*ap
      q=q->parent;
      if(q)
          i=Search(q,rx->key);//在双亲结点*q中查找rx->key的插入位置
      }//else
   }//while
if(!finished) //T是空树(参数q初值为NULL)或根结点已分裂为
              //结点*q和*ap
NewRoot(T,rx,ap);    
}//InsertBTree

Result SearchBTree(BTree T,KeyType K)
{// 在m阶B树T上查找关键字K,返回结果(pt,i,tag)。若查找成功,则特征值
// tag=1,指针pt所指结点中第i个关键字等于K;否则特征值tag=0,等于K的
// 关键字应插入在指针Pt所指结点中第i和第i+1个关键字之间。
BTree p=T,q=NULL; //初始化,p指向待查结点,q指向p的双亲
int found=false;
int i=0;
Result r;
while(p&&!found)
   {
   i=Search(p,K);//p->node[i].key≤K<p->node[i+1].key
   if(i>0&&p->node[i].key==K)
       found=true;
   else
     {
     q=p;
     p=p->node[i].ptr;//在子树中继续查找
     }//else
    }//while
   r.i=i;
   if(found)
     {
      r.pt=p;
      r.tag=1;
     }//if
   else
      {
       r.pt=q;
       r.tag=0;
      }//else
    return r;
}//SearchBTree

void print(BTNode c,int i) // TraverseDSTable()调用的函数
 {
   printf("(%d,%s)",c.node[i].key,c.node[i].recptr->others.info);
 }//print
void TraverseDSTable(BTree DT,void(*Visit)(BTNode,int))
{// 初始条件: 动态查找表DT存在,Visit是对结点操作的应用函数
// 操作结果: 按关键字的顺序对DT的每个结点调用函数Visit()一次且至多一次
int i;
if(DT) //非空树
    {
      if(DT->node[0].ptr) // 有第0棵子树
         TraverseDSTable(DT->node[0].ptr,Visit);
      for(i=1;i<=DT->keynum;i++)
        {
         Visit(*DT,i);
         if(DT->node[i].ptr) // 有第i棵子树
         TraverseDSTable(DT->node[i].ptr,Visit);
        }//for
    }//if
}//TraverseDSTable

void InputBR(BTree &t,Record r[])
{
Result s;    
for(int i=0;i<N;i++)
   {
     s=SearchBTree(t,r[i].key);
     if(!s.tag)
       InsertBTree(t,&r[i],s.pt,s.i);
   }
}//InputBR
void UserSearch(BTree t)
{
int i;
Result s;
printf("\n请输入待查找记录的关键字: ");
scanf("%d",&i);
s=SearchBTree(t,i);
if(s.tag)
print(*(s.pt),s.i);
else
printf("没找到");
printf("\n");
}//UserSearch
void DeleteIt(BTree t,BTNode *dnode,int id)
{
if(dnode->keynum>=ceil(m/2))
   {
    dnode->keynum--;
    dnode->node[id].ptr=NULL;
   }//if被删关键字Ki所在结点的关键字数目不小于ceil(m/2),则只需从结点中删除Ki和相应指针Ai,树的其它部分不变。
else if((dnode->keynum==(ceil(m/2)-1))&&((id+1)<(m-1))&&dnode->parent->node[id+1].ptr->keynum>(ceil(m/2)-1))
   {
    for(int i=1;i<m&&dnode->parent->node[i].key < dnode->parent->node[id+1].ptr->node[1].key;i++)
        dnode->node[i].key=dnode->parent->node[i].key;
    dnode->parent->node[1].key=dnode->parent->node[id+1].ptr->node[1].key;
    (dnode->parent->node[id+1].ptr->keynum)--;
   }//else if 被删关键字Ki所在结点的关键字数目等于ceil(m/2)-1,则需调整。本次为与右兄弟调整
else if((dnode->keynum==(ceil(m/2)-1))&&((id-1)>0    )&&dnode->parent->node[id-1].ptr->keynum>(ceil(m/2)-1))
   {
    for(int i=1;i<m&&dnode->parent->node[i].key > dnode->parent->node[id-1].ptr->node[dnode->parent->node[id-1].ptr->keynum].key;i++)
        dnode->node[i].key=dnode->parent->node[i].key;
    dnode->parent->node[1].key=dnode->parent->node[id-1].ptr->node[dnode->parent->node[id-1].ptr->keynum].key;
    (dnode->parent->node[id-1].ptr->keynum)--;
   }//2-else if被删关键字Ki所在结点的关键字数目等于ceil(m/2)-1,则需调整。本次为与左兄弟调整
else if((dnode->keynum==(ceil(m/2)-1))&&((id+1)<(m-1))&&dnode->parent->node[id+1].ptr->keynum==(ceil(m/2)-1))
   {
    do
      {
        BTree tmp;
        tmp=dnode;
       dnode->parent->node[id+1].ptr->node[2]=dnode->parent->node[id+1].ptr->node[1];
       dnode->parent->node[id+1].ptr->node[1]=dnode->parent->node[1];
       dnode->parent->node[id+1].ptr->keynum++;
       dnode->parent->node[id+1].ptr->node[0].ptr=dnode->node[1].ptr;
       dnode->parent->keynum--;
       dnode->parent->node[id].ptr=NULL;
       tmp=dnode;
       if(dnode->parent->keynum>=(ceil(m/2)-1))
           dnode->parent->node[1]=dnode->parent->node[2];
       dnode=dnode->parent;
       free(tmp);
      }while(dnode->keynum<(ceil(m/2)-1));    //双亲中keynum<
   }//3-else if被删关键字Ki所在结点和其相邻兄弟结点中的的关键字数目均等于ceil(m/2)-1,本次假设右兄弟存在
else if((dnode->keynum==(ceil(m/2)-1))&&(id-1)>0      &&dnode->parent->node[id-1].ptr->keynum==(ceil(m/2)-1))
   {
    do
      {
        BTree tmp;
        tmp=dnode;
       dnode->parent->node[id-1].ptr->node[2]=dnode->parent->node[id-1].ptr->node[1];
       dnode->parent->node[id-1].ptr->node[1]=dnode->parent->node[1];
       dnode->parent->node[id-1].ptr->keynum++;
       dnode->parent->node[id-1].ptr->node[0].ptr=dnode->node[1].ptr;
       dnode->parent->keynum--;
       dnode->parent->node[id].ptr=NULL;
       tmp=dnode;
       if(dnode->parent->keynum>=(ceil(m/2)-1))
           dnode->parent->node[1]=dnode->parent->node[2];
       dnode=dnode->parent;
       free(tmp);
      }while(dnode->keynum<(ceil(m/2)-1)); //双亲中keynum<
   }//4-else if被删关键字Ki所在结点和其相邻兄弟结点中的的关键字数目均等于ceil(m/2)-1,本次假设左兄弟存在
    else printf("Error!"); //出现异常
}//DeleteIt
void UserDelete(BTree t)
{
KeyType date;
Result s;
printf("Please input the date you want to delete:\n");
scanf("%d",&date);
s=SearchBTree(t,date);
if(!s.tag)  printf("Search failed,no such date\n");
else DeleteIt(t,s.pt,s.i);
}//UserDelete

int main()
{
Record r[N]={{24,"1"},{45,"2"},{53,"3"},{12,"4"},{37,"5"},
        {50,"6"},{61,"7"},{90,"8"},{100,"9"},{70,"10"},
        {3,"11"},{30,"12"},{26,"13"},{85,"14"},{3,"15"},
        {7,"16"}};    
BTree t;
InitDSTable(t);
InputBR(t,r);
printf("按关键字的顺序遍历B_树:\n");
TraverseDSTable(t,print);
UserSearch(t);
UserDelete(t);
TraverseDSTable(t,print);
DestroyDSTable(t);
return 1;
}

五、复杂度分析

B-树查找包含两种基本动作:

     ●在B-树上查找结点

     ●在结点中找关键字

前一操作在磁盘上进行,后一操作在内存进行。因此查找效率主要由前一操作决定。在磁盘上查找的次数取决于关键字结点在B-树上的层次数。

定理:若n1m3,则对任意一棵具有n个关键字的mB-树,其树高度h至多为logt((n+1)/2)+1t= ceil(m/2)。也就是说根结点到关键字所在结点的路径上涉及的结点数不超过logt((n+1)/2)+1。推理如下:

29、B-树的插入、查找、删除 - 墨涵 - 墨涵天地

29、B-树的插入、查找、删除 - 墨涵 - 墨涵天地

29、B-树的插入、查找、删除 - 墨涵 - 墨涵天地

 

 

另一套代码,可执行,附带时间测试函数。

  1. /* btrees.h */
  2. /*
  3. * 平衡多路树的一种重要方案。
  4. * 在 1970 年由 R. Bayer 和 E. McCreight 发明。
  5. */
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <time.h>
  9. #include <assert.h>
  10. #include <windows.h>
  11. #define M 1
  12. /* B 树的阶,即非根节点中键的最小数目。
  13. * 有些人把阶定义为非根节点中子树的最大数目。
  14. */
  15. typedef int typekey;
  16. typedef struct btnode { /* B-Tree 节点 */
  17. int d; /* 节点中键的数目 */
  18. typekey k[M+2]; /* 键 */
  19. char *v[M+2]; /* 值 */
  20. struct btnode *p[M+2+1]; /* 指向子树的指针 */
  21. } node, *btree;
  22. /*
  23. * 每个键的左子树中的所有的键都小于这个键,
  24. * 每个键的右子树中的所有的键都大于等于这个键。
  25. * 叶子节点中的每个键都没有子树。
  26. */
  27. /* 当 M 等于 1 时也称为 2-3 树
  28. * +----+----+
  29. * | k0 | k1 |
  30. * +-+----+----+---
  31. * | p0 | p1 | p2 |
  32. * +----+----+----+
  33. */
  34. //extern int btree_disp; /* 查找时找到的键在节点中的位置 */
  35. //extern char * InsValue; /* 与要插的键相对应的值 */
  36. int btree_disp; /* 查找时找到的键在节点中的位置 */
  37. char * InsValue; /* 与要插的键相对应的值 */
  38. int flag; /* 节点增减标志 */
  39. int btree_level; /* 多路树的高度 */
  40. int btree_count; /* 多路树的键总数 */
  41. int node_sum; /* 多路树的节点总数 */
  42. int level; /* 当前访问的节点所处的高度 */
  43. btree NewTree; /* 在节点分割的时候指向新建的节点 */
  44. typekey InsKey; /* 要插入的键 */
  45. btree search(typekey, btree);
  46. btree insert(typekey,btree);
  47. btree delete(typekey,btree);
  48. int height(btree);
  49. int count(btree);
  50. double payload(btree);
  51. btree deltree(btree);
  52. static void InternalInsert(typekey, btree);
  53. static void InsInNode(btree, int);
  54. static void SplitNode(btree, int);
  55. static btree NewRoot(btree);
  56. static void InternalDelete(typekey, btree);
  57. static void JoinNode(btree, int);
  58. static void MoveLeftNode(btree t, int);
  59. static void MoveRightNode(btree t, int);
  60. static void DelFromNode(btree t, int);
  61. static btree FreeRoot(btree);
  62. static btree delall(btree);
  63. static void Error(int,typekey);
  64. void out_txt(int * pt,int n);
  65. int *read_txt(int n);
  66. /* end of btrees.h */


 

  1. /* btrees.c */
  2. #include "btrees.h"
  3. btree search(typekey key, btree t)
  4. {
  5. int i,j,m;
  6. level=btree_level-1;
  7. while (level >= 0){
  8. for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m));
  9. if (key == t->k [ i ]){
  10. btree_disp = i;
  11. return t;
  12. }
  13. if (key > t->k [ i ]) /* i == t->d-1 时有可能出现 */
  14. i++;
  15. t = t->p[ i ];
  16. level--;
  17. }
  18. return NULL;
  19. }
  20. btree insert(typekey key, btree t)
  21. {
  22. level=btree_level;
  23. InternalInsert(key, t);
  24. if (flag == 1) /* 根节点满之后,它被分割成两个半满节点 */
  25. t=NewRoot(t); /* 树的高度增加 */
  26. return t;
  27. }
  28. void InternalInsert(typekey key, btree t)
  29. {
  30. int i,j,m;
  31. level--;
  32. if (level < 0){ /* 到达了树的底部: 指出要做的插入 */
  33. NewTree = NULL; /* 这个键没有对应的子树 */
  34. InsKey = key; /* 导致底层的叶子节点增加键值+空子树对 */
  35. btree_count++;
  36. flag = 1; /* 指示上层节点把返回的键插入其中 */
  37. return;
  38. }
  39. for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m));
  40. if (key == t->k[ i ]) {
  41. Error(1,key); /* 键已经在树中 */
  42. flag = 0;
  43. return;
  44. }
  45. if (key > t->k[ i ]) /* i == t->d-1 时有可能出现 */
  46. i++;
  47. InternalInsert(key, t->p[ i ]);
  48. if (flag == 0)
  49. return;
  50. /* 有新键要插入到当前节点中 */
  51. if (t->d < 2*M) {/* 当前节点未满 */
  52. InsInNode(t, i); /* 把键值+子树对插入当前节点中 */
  53. flag = 0; /* 指示上层节点没有需要插入的键值+子树,插入过程结束 */
  54. }
  55. else /* 当前节点已满,则分割这个页面并把键值+子树对插入当前节点中 */
  56. SplitNode(t, i); /* 继续指示上层节点把返回的键值+子树插入其中 */
  57. }
  58. /*
  59. * 把一个键和对应的右子树插入一个节点中
  60. */
  61. void InsInNode(btree t, int d)
  62. {
  63. int i;
  64. /* 把所有大于要插入的键值的键和对应的右子树右移 */
  65. for(i = t->d; i > d; i--){
  66. t->k[ i ] = t->k[i-1];
  67. t->v[ i ] = t->v[i-1];
  68. t->p[i+1] = t->p[ i ];
  69. }
  70. /* 插入键和右子树 */
  71. t->k[ i ] = InsKey;
  72. t->p[i+1] = NewTree;
  73. t->v[ i ] = InsValue;
  74. t->d++;
  75. }
  76. /*
  77. * 前件是要插入一个键和对应的右子树,并且本节点已经满
  78. * 导致分割这个节点,插入键和对应的右子树,
  79. * 并向上层返回一个要插入键和对应的右子树
  80. */
  81. void SplitNode(btree t, int d)
  82. {
  83. int i,j;
  84. btree temp;
  85. typekey temp_k;
  86. char *temp_v;
  87. /* 建立新节点 */
  88. temp = (btree)malloc(sizeof(node));
  89. /*
  90. * +---+--------+-----+-----+--------+-----+
  91. * | 0 | ...... | M | M+1 | ...... |2*M-1|
  92. * +---+--------+-----+-----+--------+-----+
  93. * |<- M+1 ->|<- M-1 ->|
  94. */
  95. if (d > M) { /* 要插入当前节点的右半部分 */
  96. /* 把从 2*M-1 到 M+1 的 M-1 个键值+子树对转移到新节点中,
  97. * 并且为要插入的键值+子树空出位置 */
  98. for(i=2*M-1,j=M-1; i>=d; i--,j--) {
  99. temp->k[j] = t->k[ i ];
  100. temp->v[j] = t->v[ i ];
  101. temp->p[j+1] = t->p[i+1];
  102. }
  103. for(i=d-1,j=d-M-2; j>=0; i--,j--) {
  104. temp->k[j] = t->k[ i ];
  105. temp->v[j] = t->v[ i ];
  106. temp->p[j+1] = t->p[i+1];
  107. }
  108. /* 把节点的最右子树转移成新节点的最左子树 */
  109. temp->p[0] = t->p[M+1];
  110. /* 在新节点中插入键和右子树 */
  111. temp->k[d-M-1] = InsKey;
  112. temp->p[d-M] = NewTree;
  113. temp->v[d-M-1] = InsValue;
  114. /* 设置要插入上层节点的键和值 */
  115. InsKey = t->k[M];
  116. InsValue = t->v[M];
  117. }
  118. else { /* d <= M */
  119. /* 把从 2*M-1 到 M 的 M 个键值+子树对转移到新节点中 */
  120. for(i=2*M-1,j=M-1; j>=0; i--,j--) {
  121. temp->k[j] = t->k[ i ];
  122. temp->v[j] = t->v[ i ];
  123. temp->p[j+1] = t->p[i+1];
  124. }
  125. if (d == M) /* 要插入当前节点的正中间 */
  126. /* 把要插入的子树作为新节点的最左子树 */
  127. temp->p[0] = NewTree;
  128. /* 直接把要插入的键和值返回给上层节点 */
  129. else { /* (d<M) 要插入当前节点的左半部分 */
  130. /* 把节点当前的最右子树转移成新节点的最左子树 */
  131. temp->p[0] = t->p[M];
  132. /* 保存要插入上层节点的键和值 */
  133. temp_k = t->k[M-1];
  134. temp_v = t->v[M-1];
  135. /* 把所有大于要插入的键值的键和对应的右子树右移 */
  136. for(i=M-1; i>d; i--) {
  137. t->k[ i ] = t->k[i-1];
  138. t->v[ i ] = t->v[i-1];
  139. t->p[i+1] = t->p[ i ];
  140. }
  141. /* 在节点中插入键和右子树 */
  142. t->k[d] = InsKey;
  143. t->p[d+1] = NewTree;
  144. t->v[d] = InsValue;
  145. /* 设置要插入上层节点的键和值 */
  146. InsKey = temp_k;
  147. InsValue = temp_v;
  148. }
  149. }
  150. t->d =M;
  151. temp->d = M;
  152. NewTree = temp;
  153. node_sum++;
  154. }
  155. btree delete(typekey key, btree t)
  156. {
  157. level=btree_level;
  158. InternalDelete(key, t);
  159. if (t->d == 0)
  160. /* 根节点的子节点合并导致根节点键的数目随之减少,
  161. * 当根节点中没有键的时候,只有它的最左子树可能非空 */
  162. t=FreeRoot(t);
  163. return t;
  164. }
  165. void InternalDelete(typekey key, btree t)
  166. {
  167. int i,j,m;
  168. btree l,r;
  169. int lvl;
  170. level--;
  171. if (level < 0) {
  172. Error(0,key); /* 在整个树中未找到要删除的键 */
  173. flag = 0;
  174. return;
  175. }
  176. for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m));
  177. if (key == t->k[ i ]) { /* 找到要删除的键 */
  178. if (t->v[ i ] != NULL)
  179. free(t->v[ i ]); /* 释放这个节点包含的值 */
  180. if (level == 0) { /* 有子树为空则这个键位于叶子节点 */
  181. DelFromNode(t,i);
  182. btree_count--;
  183. flag = 1;
  184. /* 指示上层节点本子树的键数量减少 */
  185. return;
  186. } else { /* 这个键位于非叶节点 */
  187. lvl = level-1;
  188. /* 找到前驱节点 */
  189. r = t->p[ i ];
  190. while (lvl > 0) {
  191. r = r->p[r->d];
  192. lvl--;
  193. }
  194. t->k[ i ]=r->k[r->d-1];
  195. t->v[ i ]=r->v[r->d-1];
  196. r->v[r->d-1]=NULL;
  197. key = r->k[r->d-1];
  198. }
  199. }
  200. else if (key > t->k[ i ]) /* i == t->d-1 时有可能出现 */
  201. i++;
  202. InternalDelete(key,t->p[ i ]);
  203. /* 调整平衡 */
  204. if (flag == 0)
  205. return;
  206. if (t->p[ i ]->d < M) {
  207. if (i == t->d) /* 在最右子树中发生了删除 */
  208. i--; /* 调整最右键的左右子树平衡 */
  209. l = t->p [ i ];
  210. r = t->p[i+1];
  211. if (r->d > M)
  212. MoveLeftNode(t,i);
  213. else if(l->d > M)
  214. MoveRightNode(t,i);
  215. else {
  216. JoinNode(t,i);
  217. /* 继续指示上层节点本子树的键数量减少 */
  218. return;
  219. }
  220. flag = 0;
  221. /* 指示上层节点本子树的键数量没有减少,删除过程结束 */
  222. }
  223. }
  224. /*
  225. * 合并一个节点的某个键对应的两个子树
  226. */
  227. void JoinNode(btree t, int d)
  228. {
  229. btree l,r;
  230. int i,j;
  231. l = t->p[d];
  232. r = t->p[d+1];
  233. /* 把这个键下移到它的左子树 */
  234. l->k[l->d] = t->k[d];
  235. l->v[l->d] = t->v[d];
  236. /* 把右子树中的所有键值和子树转移到左子树 */
  237. for (j=r->d-1,i=l->d+r->d; j >= 0 ; j--,i--) {
  238. l->k[ i ] = r->k[j];
  239. l->v[ i ] = r->v[j];
  240. l->p[ i ] = r->p[j];
  241. }
  242. l->p[l->d+r->d+1] = r->p[r->d];
  243. l->d += r->d+1;
  244. /* 释放右子树的节点 */
  245. free(r);
  246. /* 把这个键右边的键和对应的右子树左移 */
  247. for (i=d; i < t->d-1; i++) {
  248. t->k[ i ] = t->k[i+1];
  249. t->v[ i ] = t->v[i+1];
  250. t->p[i+1] = t->p[i+2];
  251. }
  252. t->d--;
  253. node_sum--;
  254. }
  255. /*
  256. * 从一个键的右子树向左子树转移一些键,使两个子树平衡
  257. */
  258. void MoveLeftNode(btree t, int d)
  259. {
  260. btree l,r;
  261. int m; /* 应转移的键的数目 */
  262. int i,j;
  263. l = t->p[d];
  264. r = t->p[d+1];
  265. m = (r->d - l->d)/2;
  266. /* 把这个键下移到它的左子树 */
  267. l->k[l->d] = t->k[d];
  268. l->v[l->d] = t->v[d];
  269. /* 把右子树的最左子树转移成左子树的最右子树
  270. * 从右子树向左子树移动 m-1 个键+子树对 */
  271. for (j=m-2,i=l->d+m-1; j >= 0; j--,i--) {
  272. l->k[ i ] = r->k[j];
  273. l->v[ i ] = r->v[j];
  274. l->p[ i ] = r->p[j];
  275. }
  276. l->p[l->d+m] = r->p[m-1];
  277. /* 把右子树的最左键提升到这个键的位置上 */
  278. t->k[d] = r->k[m-1];
  279. t->v[d] = r->v[m-1];
  280. /* 把右子树中的所有键值和子树左移 m 个位置 */
  281. r->p[0] = r->p[m];
  282. for (i=0; i<r->d-m; i++) {
  283. r->k[ i ] = r->k[i+m];
  284. r->v[ i ] = r->v[i+m];
  285. r->p[ i ] = r->p[i+m];
  286. }
  287. r->p[r->d-m] = r->p[r->d];
  288. l->d+=m;
  289. r->d-=m;
  290. }
  291. /*
  292. * 从一个键的左子树向右子树转移一些键,使两个子树平衡
  293. */
  294. void MoveRightNode(btree t, int d)
  295. {
  296. btree l,r;
  297. int m; /* 应转移的键的数目 */
  298. int i,j;
  299. l = t->p[d];
  300. r = t->p[d+1];
  301. m = (l->d - r->d)/2;
  302. /* 把右子树中的所有键值和子树右移 m 个位置 */
  303. r->p[r->d+m]=r->p[r->d];
  304. for (i=r->d-1; i>=0; i--) {
  305. r->k[i+m] = r->k[ i ];
  306. r->v[i+m] = r->v[ i ];
  307. r->p[i+m] = r->p[ i ];
  308. }
  309. /* 把这个键下移到它的右子树 */
  310. r->k[m-1] = t->k[d];
  311. r->v[m-1] = t->v[d];
  312. /* 把左子树的最右子树转移成右子树的最左子树 */
  313. r->p[m-1] = l->p[l->d];
  314. /* 从左子树向右子树移动 m-1 个键+子树对 */
  315. for (i=l->d-1,j=m-2; j>=0; j--,i--) {
  316. r->k[j] = l->k[ i ];
  317. r->v[j] = l->v[ i ];
  318. r->p[j] = l->p[ i ];
  319. }
  320. /* 把左子树的最右键提升到这个键的位置上 */
  321. t->k[d] = l->k[ i ];
  322. t->v[d] = l->v[ i ];
  323. l->d-=m;
  324. r->d+=m;
  325. }
  326. /*
  327. * 把一个键和对应的右子树从一个节点中删除
  328. */
  329. void DelFromNode(btree t, int d)
  330. {
  331. int i;
  332. /* 把所有大于要删除的键值的键左移 */
  333. for(i=d; i < t->d-1; i++) {
  334. t->k[ i ] = t->k[i+1];
  335. t->v[ i ] = t->v[i+1];
  336. }
  337. t->d--;
  338. }
  339. /*
  340. * 建立有两个子树和一个键的根节点
  341. */
  342. btree NewRoot(btree t)
  343. {
  344. btree temp;
  345. temp = (btree)malloc(sizeof(node));
  346. temp->d = 1;
  347. temp->p[0] = t;
  348. temp->p[1] = NewTree;
  349. temp->k[0] = InsKey;
  350. temp->v[0] = InsValue;
  351. btree_level++;
  352. node_sum++;
  353. return(temp);
  354. }
  355. /*
  356. * 释放根节点,并返回它的最左子树
  357. */
  358. btree FreeRoot(btree t)
  359. {
  360. btree temp;
  361. temp = t->p[0];
  362. free(t);
  363. btree_level--;
  364. node_sum--;
  365. return temp;
  366. }
  367. void Error(int f,typekey key)
  368. {
  369. if (f)
  370. printf("Btrees error: Insert %d!\n",key);
  371. else
  372. printf("Btrees error: delete %d!\n",key);
  373. }
  374. int height(btree t)
  375. {
  376. return btree_level;
  377. }
  378. int count(btree t)
  379. {
  380. return btree_count;
  381. }
  382. double payload(btree t)
  383. {
  384. if (node_sum==0)
  385. return 1;
  386. return (double)btree_count/(node_sum*(2*M));
  387. }
  388. btree deltree (btree t)
  389. {
  390. level=btree_level;
  391. btree_level = 0;
  392. return delall(t);
  393. }
  394. btree delall(btree t)
  395. {
  396. int i;
  397. level--;
  398. if (level >= 0) {
  399. for (i=0; i < t->d; i++)
  400. if (t->v[ i ] != NULL)
  401. free(t->v[ i ]);
  402. if (level > 0)
  403. for (i=0; i<= t->d ; i++)
  404. t->p[ i ]=delall(t->p[ i ]);
  405. free(t);
  406. }
  407. return NULL;
  408. }
  409. /* end of btrees.c */
  410. void out_txt(int * pt,int n)
  411. {
  412. FILE *fp_b;
  413. int i;
  414. if((fp_b=fopen("btree2.txt","a"))==NULL)
  415. {
  416. printf("cannot open this file\n");
  417. exit(0);
  418. }
  419. //fprintf(fp_b,"%d\t",bucket_volumes);
  420. for (i=0;i<n;i++)
  421. {
  422. fprintf(fp_b,"%d\t",pt[i]);
  423. }
  424. fprintf(fp_b,"\r");
  425. }
  426. int *read_txt(int n)
  427. {
  428. FILE *fp;
  429. int i;
  430. int *a=(int *)malloc(n*sizeof(int));
  431. if ((fp = fopen("F:\\text_data.txt","rt")) == NULL)
  432. {
  433. printf("open file failed!\n");
  434. exit(1);
  435. }
  436. for (i=0; i<n; i++)
  437. {
  438. fscanf(fp,"%d", &a[i]);
  439. // fprintf(fp,"%d ",a[i]);
  440. }
  441. fclose(fp);
  442. return a;
  443. }


 

  1. #include "btrees.h"
  2. #define key_num 1000
  3. void main()
  4. {
  5. int * key_array;
  6. int i;
  7. int pt[5];
  8. btree root,temp;
  9. long long sf1,sf2,sf3;
  10. LARGE_INTEGER f1,f2,f3,f5,f4,f6;
  11. InsValue = NULL; /* 与要插的键相对应的值 */
  12. /* 节点增减标志 */
  13. btree_level = 0; /* 多路树的高度 */
  14. btree_count = 0; /* 多路树的键总数 */
  15. node_sum = 0; /* 多路树的节点总数 */
  16. key_array=read_txt(key_num);
  17. temp = (btree)malloc(sizeof(node));
  18. temp->d = 0;
  19. temp->p[0] = NewTree;
  20. temp->p[1] = NewTree;
  21. temp->k[0] = InsKey;
  22. temp->v[0] = InsValue;
  23. root=temp;
  24. QueryPerformanceCounter(&f1);
  25. for (i=0;i<key_num;i++)
  26. {
  27. root=insert(key_array[i],root);
  28. }
  29. QueryPerformanceCounter(&f2);
  30. QueryPerformanceCounter(&f3);
  31. for (i=0;i<key_num;i++)
  32. {
  33. search(key_array[i],root);
  34. }
  35. QueryPerformanceCounter(&f4);
  36. QueryPerformanceCounter(&f5);
  37. for (i=0;i<key_num;i++)
  38. {
  39. root=delete(key_array[i],root);
  40. }
  41. QueryPerformanceCounter(&f6);
  42. sf1=f2.QuadPart-f1.QuadPart-750;
  43. sf2=f4.QuadPart-f3.QuadPart-750;
  44. sf3=f6.QuadPart-f5.QuadPart-750;
  45. pt[0]=M+2;
  46. pt[1]=key_num;
  47. pt[2]=(sf1/1000)-7;
  48. pt[3]=(sf2/1000)-7;
  49. pt[4]=(sf3/1000)-7;
  50. out_txt(pt,5);
  51. Sleep(1000);
  52. }


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/766287
推荐阅读
相关标签
  

闽ICP备14008679号