1.B-树的定义如下:
一颗2m+1阶(每个结点可包括2m个关键字,2m+1个指针)的B-树,度为2m+1的树:
(1).树中每个结点最多有2m+1棵子树,且除根结点外的所有非叶子结点至少有m+1棵树,而根结点至少有两棵树;
(2).所有叶子结点均在最后一层上;
(3).除叶子结点外的每个结点结构如图所示:
(4).所有叶子结点中的指针域为空。
2.B-树类:
#include <iostream>
#define M 2
using namespace std;
//定义B-树中的结点类型
template <class T>
struct mb1node
{
int num; //记录结点中的关键字个数
mb1node *prt;//指向父结点的指针
T key[2*M];//2m个关键字域
mb1node *link[2*M+1];//2m+1个指向各子树的指针
};
//定义B-树类
template <class T>
class MB1
{
private:
mb1node<T> *BTH;//B-树根结点指针
public:
MB1(){BTH=NULL;return;}//初始化
mb1node<T> *MB1_search(T,int *,int *);//查找
/* mb1node<T> *MB1_search(T);*/
void MB1_insert(T);//插入
void MB1_delete(T);//删除
void MB1_prt();//按值大小输出B-树
};
static int k=1,flag=0;
//在B-树中查找元素x所在结点的存储位置及在该结点中的关键字序号k
//函数返回结点存储空间首地址,flag=0表示查找失败
template <class T>
mb1node<T> * MB1<T>::MB1_search(T x,int *k,int *flag)
{
mb1node<T> *p,*q;
p=BTH;*flag=0;q=p;
while((p!=NULL)&&(*flag==0))//未找到叶子结点且未找到该元素
{
*k=1;q=p;
while((*k<q->num)&&(q->key[*k-1]<x))//与各关键字比较
*k=*k+1;
if(q->key[*k-1]==x) //查找成功
*flag=1;
else if((*k==q->num)&&(q->key[*k-1]<x)) //向下搜索
p=q->link[*k];
else
{
p=q->link[*k-1];*k=*k-1; //向下搜索
}
}
return q;
}
//在B-树种插入x
template <class T>
void MB1<T>::MB1_insert(T x)
{
int flag,j,k,t;
T y;
mb1node<T> *p,*q,*u,*s;
if (BTH==NULL) //B-树为空
{
p=new mb1node<T>;
p->num=1;
p->key[0]=x;
p->prt=NULL;
for(j=1;j<=2*M+1;j++)
p->link[j-1]=NULL;
BTH=p; //B-树根结点指针
return;
}
q=(mb1node<T> *)MB1_search(x,&k,&flag);//寻找插入位置
if(flag==1) //已有元素x,无需插入
{
cout<<"ERR!\n";
return;
}
p=NULL;
t=0; //未插入完标记
while(t==0) //未插入完
{
if(k==(q->num))//插入到结点q最后
{
y=x; //记录结点q的最后应插入的关键字
u=p;//记录结点q中最后一个向下指针
}
else//插入到结点q中间某位置处
{
y=q->key[q->num-1];//记录结点q中的最后一个关键字
u=q->link[q->num];//记录结点q中的最后一个向下指针
for(j=(q->num)-1;j>=k+1;j--)//结点q中最后第二个关键字到插入位置处的关键字以及对应的向下指针均后移一位
{
q->key[j]=q->key[j-1];
q->link[j+1]=q->link[j];
}
q->key[k]=x; //在插入位置处插入关键字x
q->link[k+1]=p;//在插入位置后插入关键字x的向下指针
if(p!=NULL)
p->prt=q;//改变结点p中指向父结点的指针
}
if(q->num<2*M) //结点q中关键字未满,可直接插入
{
q->num=(q->num)+1;//关键字个数加1
q->key[(q->num)-1]=y;//插入到q的最后
q->link[q->num]=u;//将记录的向下指针插到q的最后
if(u!=NULL)
u->prt=q;//改变结点u中指向父结点的指针
t=1;//置插入完成标记
}
else//结点q中关键字已满,应进行分裂
{
p=new mb1node<T>;//申请一个新结点
p->num=M;//新结点存放结点q中的一半关键字
q->num=M;//结点q中保留原一半关键字
p->prt=q->prt;//结点q的父结点也是新结点p的父节点
x=q->key[M];//记录原结点q中的中间关键字,应插入到父结点
for(j=1;j<=M-1;j++)//
{
p->key[j-1]=q->key[M+j];//将原结点q中的后半部的关键字河向下指针复制到结点p中
p->link[j-1]=q->link[M+j];//
if(q->link[M+j]!=NULL)//
(q->link[M+j])->prt=p;//改变子结点中指向父结点的指针
}
p->link[M-1]=q->link[2*M];//将q中的最后一个指针复制到p中
if(q->link[2*M]!=NULL)//
(q->link[2*M])->prt=p;//改变子结点中指向父结点的指针
p->key[M-1]=y;//将记录的关键字插入到p的最后
p->link[M]=u;//将记录的向下指针插入到p的最后
if(u!=NULL)//
u->prt=p;//改变结点u中指向父结点的指针
for(j=M+2;j<=2*M+1;j++)//
{
q->link[j-1]=NULL;//置结点q后半部分指针为空
p->link[j-1]=NULL;//置结点p后半部分指针为空
}
if(q->prt==NULL)//q为根结点
{
s=new mb1node<T>;//
s->key[0]=x;//插入原结点q分裂时的中间关键字x
s->link[0]=q; //第一个指针指向q
s->link[1]=p;//第二个指针指向p
s->num=1;//根结点中关键字个数为1
s->prt=NULL;//根节点无父结点
q->prt=s;p->prt=s;//结点p和q的父结点均为根结点s
for(j=3;j<=2*M+1;j++)//
s->link[j-1]=NULL;//置根结点中后面的指针为空
BTH=s;//s为B-树的根结点
t=1;//置插入完成标志
}
else//q不是根结点
{
q=q->prt;//原结点q分裂时的中间关键字x应插入到q的父结点
k=1;//
while((k<=q->num)&&(q->key[k-1]<x))//寻找插入位置
k=k+1;//
k=k-1;//
}
}
}
return;
}
//在B-树中删除x
template <class T>
void MB1<T>::MB1_delete(T x)
{
int flag,j,k,t;
T y;
mb1node<T> *u,*s=NULL,*p,*q;
q=(mb1node<T> *)MB1_search(x,&k,&flag);
if(flag==0)
{
cout<<"not this key!\n";
return;
}
p=q->link[k];
if(p!=NULL)
{
while(p->link[0]!=NULL)
p=p->link[0];
q->key[k-1]=p->key[0];
k=1;q=p;
}
for(j=k;j<=q->num-1;j++)
q->key[j-1]=q->key[j];
q->num=q->num-1;
while((q!=BTH)&&(q->num<M))
{
p=q->prt;j=1;
while(p->link[j-1]!=q)
j=j+1;
if((j<=p->num)&&((p->link[j])->num>M))
{
s=p->link[j];
y=s->key[0];
u=s->link[0];
for(k=1;k<=s->num-1;k++)
{
s->key[k-1]=s->key[k];
s->link[k-1]=s->link[k];
}
s->link[s->num-1]=s->link[s->num];
s->link[s->num]=NULL;
s->num=s->num-1;
q->num=q->num+1;
q->key[q->num-1]=p->key[j-1];
p->key[j-1]=y;
q->link[q->num]=u;
if(u!=NULL)
u->prt=q;
}
else if((j>1)&&((p->link[j-2])->num>M))
{
s=p->link[j-2];
q->num=q->num+1;
q->link[q->num]=q->link[q->num-1];
for(k=q->num-1;k>=1;k--)
{
q->key[k]=q->key[k-1];
q->link[k]=q->link[k-1];
}
q->key[0]=p->key[j-2];
q->link[0]=s->link[s->num];
u=s->link[s->num];
if(u!=NULL)
u->prt=q;
p->key[j-2]=s->key[s->num-1];
s->link[s->num]=NULL;
s->num=s->num-1;
}
else
{
if(j==p->num+1)
{
q=p->link[j-2];
s=p->link[j-1];
j=j-1;
}
else s=p->link[j];
q->key[q->num]=p->key[j-1];
t=q->num+1;
for(k=1;k<=s->num;k++)
{
q->key[t+k-1]=s->key[k-1];
q->link[t+k-1]=s->link[k-1];
u=s->link[k-1];
if(u!=NULL)
u->prt=q;
}
q->link[t+s->num]=s->link[s->num];
u=s->link[s->num];
if(u!=NULL)
u->prt=q;
q->num=2*M;
delete s;
for(k=j;k<=p->num-1;k++)
{
p->key[k-1]=p->key[k];
p->link[k]=p->link[k+1];
}
p->num=p->num-1;
s=q;q=p;
}
}
if((q==BTH)&&(q->num==0))
{
delete BTH;
if (s!=NULL)
{
BTH=s;BTH->prt=NULL;
}
else
{
BTH=NULL;delete s;
}
}
return;
}
//按值大小输出B-树
template <class T>
void MB1<T>::MB1_prt()
{
MB1_out(BTH);
return;
}
template <class T>
static MB1_out(mb1node<T> *p)
{
int n;
if(p!=NULL)
{
for(n=0;n<p->num;n++)
{
MB1_out(p->link[n]);
cout<<p->key[n]<<endl;
}
MB1_out(p->link[n]);
}
return 0;
}
3.具体事例:
在一个空B-树中国一层插入以下关键字:05,06,09,79,52,23,34,12,76,18,52,45,再按值大小输出,然后再按插入顺序删除前六个关键字,然后再按值输出。
#include "MB1.h"
int main()
{
int x,k;
int d[12]={05,06,09,79,52,23,34,12,76,18,52,45};
MB1<int> mb1;
for(k=0;k<12;k++)
{
x=d[k];
mb1.MB1_insert(x);
}
cout<<"第1次按值大小输出B-树:"<<endl;
mb1.MB1_prt();
for(k=0;k<6;k++)
{
x=d[k];mb1.MB1_delete(x);
}
cout<<"第2次按值大小输出B-树:"<<endl;
mb1.MB1_prt();
return 0;
}
4.实验结果: