B树又叫平衡多路查找树,是一种组织和维护外存文件系统非常有效的数据结构
一棵m阶的B树(m叉树)的特性如下:
树中每个结点最多含有m个孩子(m>=2);
除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]=(m-1)/2个孩子(其中ceil(x)是一个取上限的函数);
若根结点不是叶子结点,则至少有2个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);
所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部接点或查询失败的接点,
实际上这些结点不存在,指向这些结点的指针都为null);
每个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
a)
b)
c)
* B树的插入算法
*将关键字K插入到B树中分两步进行:
(1)、利用前述的B树的查找算法找出该关键字的插入结点(插入结点一定是叶子结点)
(2)、判断该结点是否有空位置,即判断该结点是有满足n
直接插入即可(要保持该结点的关键字仍然有序);
若n=m-1,说明该结点已经没有空位置,要将该结点分裂成两个.分裂的做法是,取一个
新结点,把原结点上的关键字和k按升序排列,从中间位置((m+1)/2处)把关键字(
不包含中间关键字)分裂成两部分,左部分所含关键字放在旧节点中,右部分所含关键
字放在新结点中,中间关键字连同新结点的地址插入到父节点中。
如果父节点的关键字数也满了,那就向上继续分裂,最多是做到把根节点也分裂,产生
新的根节点。
*/
/*
*B树的删除算法
*要使得删除后的结点满足n>=(m-1)/2,将涉及到结点的"合并"问题。
*在B树中删除关键字K的过程分两步完成:
(1)、查找出该关键字所在结点
(2)、在结点上删除关键字k分两种情况:一是在叶子结点上删除、二在非叶子结点上删除
1)、在非叶子结点上删除关键字k的过程:假设要删除的关键字key[i],在删去该关键
字后,以该结点的ptr[i]所指向的子树中的最小关键字key[min]来代替被删关键
字key[i]所在的位置(注意ptr[i]所指子树中的最小关键字key[min]一定是在叶子
节点上),然后再也指针ptr[i]所指结点为根节点查找并删除key[min](即以ptr[i]
所指的结点为B树的根节点,key[min]为删除结点,然后调用B树的删除算法),这样
也就把在非叶子结点上删除关键字k的问题转化为在叶子结点上面删除关键字key[min]的问题。
2)、在叶子结点上删除关键字共分以下三种情况:
NO.1:如果被删除的结点的关键字数大于(m-1)/2,说明删去该关键字后,该结点仍满
足B树的定义,可以直接删除.
NO.2:如果被删除的结点的关键字数等于(m-1)/2,说明删去该节点后关键字数将不满足
B树的定义,此时若该结点的左(或右)兄弟结点的关键字树大于(m-1)/2,则把该
结点的左(或右)兄弟结点中最大(或最小)的关键字上移到双亲结点中,同时把双
亲结点中大于(或小于)上移关键字的关键字下移到要删除的关键字结点中,这样
删去该关键字后,该结点的以及它的左(或右)兄弟仍满足B树的定义.
NO.3:如果被删除的结点的关键字数等于(m-1)/2,并且该结点的左右兄弟(如果存在的话)的关键字数目
也都为(m-1)/2,这时,需要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的
关键字合并成一个新的结点。如果因此双亲结点中的关键字数小于(m-1)/2,则对双亲节点做同样
的处理,以至于可能对根结点做同样的处理以至于整棵树减少一层。
代码如下:
#include<iostream>
#include<queue>
using namespace std;
#define MAX_BTNODE 10
#define DEGREE 4
struct BTNode
{
int keynum; //当前存储关键字的个数
char key[MAX_BTNODE]; //当前存储节点的关键字,以非降序存放
bool leaf; //True为叶子,false为内节点
BTNode *child[MAX_BTNODE]; //指向子女的指针,比关键字多1个
BTNode(int num,bool isleaf):keynum(num),leaf(isleaf){} //构造函数,初始化关键字数与是否为叶子标记
};
class B_Tree
{
public:
B_Tree();
void B_Tree_Init(char *key,int length); //初始化空B-Tree,其中key是初始化时要记录的关键字
void B_Tree_Insert(char key); //向B-Tree中插入关键字
void B_Tree_Insert_NonFull(BTNode *_root,char key); //向B-Tree中插入关键字,用于辅助B_Tree_Insert函数
void B_Tree_Split_Child(BTNode *_root,int i,BTNode *child); //当B-Tree某节点已经超过了MAX_BTNODE,对B-Tree进行分裂,其中i为要插入到父节点的位置
void B_Tree_Delete(char key); //在B-Tree某节点删除key关键字
bool B_Tree_Delete_NonLess(BTNode *_root,char key); //在B-Tree某节点删除key关键字,用于辅助B_Tree_Delete函数
void B_Tree_Move_From_Left(BTNode *_root,int location); //在该节点,把key左边的前驱添加到key所在的树上
void B_Tree_Move_From_Right(BTNode *_root,int location); //在该节点,把key右边的后继添加到key所在的树上
void B_Tree_Combine(BTNode *_root,int location); //合并两个子树,两棵子树的keynum均为Degree - 1
BTNode* B_Tree_Search(BTNode *_root,char key,int &location); //在B-Tree中搜索关键字key,其中location为了记录所在节点中第几个位置
bool B_Tree_Search_BTNode(BTNode *_root,char key,int &location); //只在本节点进行搜索关键字key
void B_Tree_Print_Order(BTNode *_root); //按关键字大小进行输出
void B_Tree_Print_Layer(); //对每层关键字进行输出
void B_Tree_Print_BTNode(BTNode *_root); //打印每个节点的关键字
void B_Tree_QueuePush(BTNode *_root,queue<BTNode*> *btnqueue); //对每个节点的孩子进行进栈
BTNode* B_Tree_Root();
private:
void B_Tree_Create(); //构建一棵空B-Tree
BTNode *root;
};
B_Tree::B_Tree()
{
B_Tree_Create();
}
void B_Tree::B_Tree_Create()
{
root = new BTNode(0, true);
}
void B_Tree::B_Tree_Init(char *key,int length)
{
int i = 0;
while(i < length)
{
B_Tree_Insert(key[i]);
i++;
}
}
void B_Tree::B_Tree_Insert(char key)
{
BTNode *r = root;
if(r->keynum == 2 * DEGREE - 1)
{
BTNode *newroot = new BTNode(0,false);
root = newroot;
newroot->child[0] = r;
B_Tree_Split_Child(newroot,0,r); //判断根节点是否满了,其他内节点及叶节点满不满都交给B_Tree_Insert_NonFull
B_Tree_Insert_NonFull(newroot,key);
}
else
B_Tree_Insert_NonFull(root,key);
}
void B_Tree::B_Tree_Insert_NonFull(BTNode *_root,char key)
{
int i = _root->keynum - 1;
if(_root->leaf)
{
while((i >= 0) && (key < _root->key[i]))
{
_root->key[i + 1] = _root->key[i];
i--;
}
_root->key[i + 1] = key;
_root->keynum++;
}
else
{
while((i >= 0) && (key < _root->key[i]))
i--;
i++; //子孩子是从0开始
if(_root->child[i]->keynum == 2 * DEGREE - 1)
{
B_Tree_Split_Child(_root,i,_root->child[i]);
if(key > _root->key[i + 1]) //是孩子分裂过后才需要做这一步
i++;
}
B_Tree_Insert_NonFull(_root->child[i],key);
}
}
void B_Tree::B_Tree_Split_Child(BTNode *_root,int i,BTNode *child)
{
BTNode *newNode = new BTNode(0,child->leaf);
int j;
for(j = 0; j < DEGREE - 1; j++) //复制child后半段元素,共DEGREE - 1个,正好满孩子的时候,数组下标为2*DEGREE - 2
{
newNode->key[j] = child->key[j + DEGREE];
}
newNode->keynum = j;
if(!newNode->leaf)
{
j--; //下标从0开始
for(j = 0; j <= DEGREE - 1; j++) //注意这儿跟上面复制关键字的区别,要多复制一个,最后一个小标为2*DEGREE - 1
{
newNode->child[j] = child->child[j + DEGREE];
}
}
child->keynum -= DEGREE; //child的总数加上newNode的总数是2*Degree - 2,中间那个分裂到父节点i处
for(j = _root->keynum - 1; j >= i; j--) //_root有keynum个关键字,从keynum-1开始往前逐个往后移动
{
_root->key[j + 1] = _root->key[j];
}
_root->key[i] = child->key[child->keynum];
for(j = _root->keynum; j > i; j--) //_root有keynum+1个关键字,从keynum开始往前逐个往后移动
{
_root->child[j + 1] = _root->child[j];
}
_root->child[i + 1] = newNode; //只需添加比新增节点的右孩子
_root->keynum++;
}
BTNode* B_Tree::B_Tree_Search(BTNode *_root,char key,int &location)
{
if(location < 1)
location = 1;
int i = 0;
while((i <_root->keynum)&&(key > _root->key[i]))
i++;
if((i <_root->keynum)&&(key == _root->key[i]))
{
cout<<"we find "<<key<<" in layer "<<location<<" no. "<<(i + 1)<<endl;
location = i;
return _root;
}
if(_root->leaf)
return NULL;
else
return B_Tree_Search(_root->child[i],key,++location);
}
bool B_Tree::B_Tree_Search_BTNode(BTNode *_root,char key,int &location)
{
location = 0;
while(location < _root->keynum && _root->key[location] < key)
location++;
if(location < _root->keynum && _root->key[location] == key)
return true;
else
return false;
}
void B_Tree::B_Tree_Print_Layer() //层次遍历
{
int i,layer,layernodenum,nodekeynum;
BTNode *temp;
bool flag = false;
queue<BTNode*> keyqueue;
keyqueue.push(root);
layer = 0;
nodekeynum = 1;
layernodenum = 0;
//layernodenum = root->keynum + 1;
cout<<"this "<<layer<<" layer : ";
while(!keyqueue.empty())
{
if(flag == true)
{
cout<<endl;
cout<<"this "<<layer<<" layer : ";
flag = false;
}
temp = keyqueue.front();
keyqueue.pop();
B_Tree_Print_BTNode(temp);
B_Tree_QueuePush(temp,&keyqueue);
nodekeynum--;
layernodenum += temp->keynum + 1;
if(nodekeynum == 0)
{
flag = true;
nodekeynum = layernodenum;
layer++;
layernodenum = 0;
}
}
cout<<endl;
}
void B_Tree::B_Tree_Print_BTNode(BTNode *_root)
{
int i;
for(i = 0; i < _root->keynum; i++)
{
cout<<_root->key[i]<<" ";
}
cout<<" ";
}
void B_Tree::B_Tree_QueuePush(BTNode *_root,queue<BTNode*> *btnqueue)
{
if(!_root->leaf)
{
BTNode *temp;
for(int i = 0;i <= _root->keynum; i++)
{
temp = _root->child[i];
btnqueue->push(temp);
}
}
}
BTNode* B_Tree::B_Tree_Root()
{
return this->root;
}
void B_Tree::B_Tree_Print_Order(BTNode *_root)
{
if(_root->leaf)
B_Tree_Print_BTNode(_root);
else
{
int i;
for(i = 0; i <= _root->keynum; i++)
{
B_Tree_Print_Order(_root->child[i]);
if(i != _root->keynum) //key的数量少于孩子的数量一个
cout<<_root->key[i]<<" ";
}
}
}
void B_Tree::B_Tree_Delete(char key)
{
if(B_Tree_Delete_NonLess(root,key))
cout<<"The key "<<key<<"is deleted!!!"<<endl;
else
cout<<"The key "<<key<<"is not exist in B_Tree!!!"<<endl;
}
bool B_Tree::B_Tree_Delete_NonLess(BTNode *_root,char key)
{
bool deleteflag;
if(_root == NULL)
return false;
else
{
int location = -1;
if((deleteflag = B_Tree_Search_BTNode(_root,key,location)) == true)
{
if(!_root->leaf) //当该节点是内节点
{
int childmaxnum = _root->child[location]->keynum;
_root->key[location] = _root->child[location]->key[childmaxnum - 1]; //找到左边节点最大的关键字进行替换
B_Tree_Delete_NonLess(_root->child[location],_root->key[location]);
}
else
{
int i;
for(i = location + 1; i < _root->keynum; i++) //删除叶节点的关键字
{
_root->key[i - 1] = _root->key[i];
}
_root->keynum--;
}
}
else
deleteflag = B_Tree_Delete_NonLess(_root->child[location],key); //如果在当前节点没找到,继续按照查找路径往下查找
if(!_root->leaf) //依次往上检查是否出现子节点的关键字不够的情况
{
if(_root->child[location]->keynum < DEGREE - 1) //分别分析在最左边跟最右边及其在其他的位置的情况
{
if(location == 0)
{
if(_root->child[location + 1]->keynum > DEGREE - 1)
B_Tree_Move_From_Right(_root,location);
else
B_Tree_Combine(_root,location);
}
else if(location == _root->keynum - 1)
{
if(_root->child[location - 1]->keynum > DEGREE - 1)
B_Tree_Move_From_Left(_root,location);
else
B_Tree_Combine(_root,location);
}
else
{
if(_root->child[location - 1]->keynum > DEGREE - 1)
B_Tree_Move_From_Left(_root,location);
else if(_root->child[location + 1]->keynum > DEGREE - 1)
B_Tree_Move_From_Right(_root,location);
else
B_Tree_Combine(_root,location);
}
}
}
return deleteflag;
}
}
void B_Tree::B_Tree_Move_From_Left(BTNode *_root,int location)
{
int i;
BTNode *temp = _root->child[location];
temp->child[temp->keynum + 1] = temp->child[temp->keynum];
for(i = temp->keynum; i > 0; i--)
{
temp->key[i] = temp->key[i - 1];
temp->child[i] = temp->child[i - 1];
}
temp->key[i] = _root->key[location - 1];
temp->keynum++;
temp = _root->child[location - 1];
_root->key[location - 1] = temp->key[temp->keynum - 1];
_root->child[location]->child[i] = temp->child[temp->keynum];
temp->keynum--;
}
void B_Tree::B_Tree_Move_From_Right(BTNode *_root,int location)
{
BTNode *temp = _root->child[location];
temp->key[temp->keynum] = _root->key[location];
temp->child[temp->keynum + 1] = _root->child[location + 1]->child[0];
temp->keynum++;
temp = _root->child[location + 1];
_root->key[location] = temp->key[0];
int i;
for(i = 1; i < temp->keynum; i++)
{
temp->key[i - 1] = temp->key[i];
temp->child[i - 1] = temp->child[i];
}
temp->child[i - 1] = temp->child[i];
}
void B_Tree::B_Tree_Combine(BTNode *_root,int location)
{
BTNode *templeft = _root->child[location];
BTNode *tempright = _root->child[location + 1];
int i,j;
templeft->key[templeft->keynum] = _root->key[location];
templeft->keynum++;
j = templeft->keynum;
for(i = 0; i < tempright->keynum;i++)
{
templeft->key[j] = tempright->key[i];
templeft->child[j] = tempright->child[i];
j++;
}
templeft->child[j] = tempright->child[i];
templeft->keynum = j;
_root->child[location + 1] = templeft;
for(i = location + 1; i < _root->keynum;i++)
{
_root->key[i - 1] = _root->key[i];
_root->child[i - 1] = _root->child[i];
}
_root->child[i - 1] = _root->child[i];
_root->keynum--;
delete tempright;
}
int main()
{
B_Tree *tree = new B_Tree();
int location;
char key[] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h',',i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
tree->B_Tree_Init(key,52);
tree->B_Tree_Print_Layer();
tree->B_Tree_Search(tree->B_Tree_Root(),'j',location);
tree->B_Tree_Print_Order(tree->B_Tree_Root());
tree->B_Tree_Delete('B');
tree->B_Tree_Print_Layer();
return 0;
}