B-树(多路平衡搜索树)的定义虽然比较简单,寥寥几条:
1.m阶B-树,最多只能有m个分支。
2.m阶B-树的关键字个数要介于[m/2](向上取整)-1和m-1之间(根节点除外)
3.关键字内部按升序排列
4.所有叶子节点一定在同一层
根据定义来实现B-树的难点在第二条,这就会导致分裂和合并的情况。
图文讲解可以看下面这个博客,非常的详细:
http://www.cnblogs.com/nullzx/
下面来展示对B-树的实现(伪代码),其实伪代码才是精髓,看了别人的实现方法才知道什么叫大道至简:
1.插入操作:
#define m 4
//B-树的数据结构
typedef struct node{
int Numbers; //节点关键字个数
ValueType val[m+1]; //节点数据
KeyType Key[m+1]; //节点键值
struct node *ptr[m+1]; //分支指针
struct node *Parent; //父节点指针
}BNode,*PBNode;
//传递查找信息
typedef struct re{
bool tag; //1表示查找到,0表示未查找到
int Index; //查找到的下标
PBNode Result; //记录最后查找的节点
}Record;
bool InsertKey(PBNode *Root,KeyType Key,ValueType Value)
{//PBNpde 是根节点,Key是键值,Value是键对应的值
//finish 是帮助判断迭代操作是否完成
bool finish;
while(!finish && p)
{
Record Re;
if(Search((*Root),&Re,Key));
if find modify Value and return;
else
{
InsertInNode(PBNode p,KeyType Key,ValueType Value,int InsertIndex);
//p是将要插入的节点(根据Search函数可以实现),InsertIndex是要插入的位置
if(p->Numbers >= m)
//如果插入后节点中的关键字大于上限那么进行分裂操作
DividNode(p);
//如果存在父节点
if(p->Parent)
//分裂后父节点的关键字个数满足条件,那么结束操作
if(p->Parent->Numbers < m-1)
finish = true;
//分裂到根节点,需要重新生成一个根节点
else
{
NewARoot();
finish = true;
}
//否则更新相关变量,继续迭代
else
Refresh();
}//else
}//while
//如果传入节点好似空则新生成一个节点
if(!p)
NewARoot();
}//InsertKey()
2、删除操作:
bool DeleteKey(PBNode *Root,KeyType Key,ValueType Value)
{
//Border是[m/2]-1向上取整,代表节点键个数的下边界
int Border = (m%2==0) ? (m/2-1):(m/2);
//帮助完成迭代的标志
bool finish = false;
if(!Search((*Root),Key))
Not found and return;
while(!finish)
{
//是叶子节点并且该节点的关键字个数本来就大于边界值,那么直接删除
if(IsLeave(p) && P->Numbers > Border)
Delete Key and finish is true;
//是叶子节点并且该节点的关键字个数小于边界值但是做或者右兄弟的关键字个数有多余
else if(IsLeave(p) && p->Numbers == Border && (p->LeftSilb->Numbers > Border || p->RightSilb->Numbers > Border))
//从左兄弟或者有兄弟借关键字,DeleteIndex表示在该节点要删除的键的位置
LeftKeyToRight(PBNode Left,PBNode Right,PBNode Parent,int DeleteIndex);
OR
RightKeyToLeft(PBNode Left,PBNode Right,PBNode Parent,int DeleteIndex);
AND
finish = true;
//是叶子节点,但是左右兄弟和本节点的关键字个数都等于边界值,那么从父节点拿关键字并且合并左
//或者右节点
else if(IsLeave(p) && p->Numbers == Border && (p->LeftSilb->Numbers == Border || p->RightSilb->Numbers == Border))
{
ParentKeyToSonAndMerge(PBNode Left,PBNode Right,PBNode Parent,Int DeleteIndex);
//从父节点借下节点之后如果父节点的关键字个数小于边界值,那么需要迭代操作
if(Parent->Numbers >= Border)
finish = true;
else
Refresh();
}
//删除的是非叶子节点
else
//和叶子节点的值左为交换,然后利用迭代删除
RotateToLeave();
}//while
}
3、查找:
//在指定节点查找,但是并不一定会查找到,有可能查找的是插入的下标
int Search(PBNode Node,char Key)
{
if(!Node)
return -1;
int i,j = -1;
//利用的原理是节点内部的关键字是按照升序排列
for(i = 0;i < Node->Numbers;i++)
{
if(Node->Key[i] < Key)
j = i;
}
return j;
}
bool SearchKey(PBNode Root,Record *Re,KeyType Key)
{//Re是用来记录查找的信息的,因为有可能查找失败(插入的时候调用)那么久返回需要插入的节点的信息
p = Root;
//要么查找到要么查找到叶子节点返回需要插入的节点的信息
while(!IsLeave(p))
{
int K = search(p,Key);
if(p->Key[k+1] == Key)
find and return true;
//继续深入子树
p = p->ptr[k+1];
}
return false;
}