关于B+树的基本定义,随便一本数据结构的书或者算法导论中都有,就不做介绍了。虽然网上和书本上都有很多对B+树的介绍,但是有很多资料对于B+树的操作或者介绍不全,或者有描写错误的地方,我这里参考这位大神的文章http://blog.csdn.net/xinghongduo/article/details/7059459,整理了B+树删除操作的所有情况。
我用visio画了删除操作可能出现的情况总结,如下图(在网页上看的话字体太小了,推荐下载到本地或者放大再看):
对应上图的删除部分代码如下所示:
void BPlusTree::delete_BPlus_tree(FILEP current, TRecord &record)
{
int i, j;
BPlusNode x;
ReadBPlusNode(current, x);
for (i = 0; i < x.nkey && record.key > x.key[i]; i++);
if (i < x.nkey && x.key[i] == record.key) //在当前节点找到关键字
{
if (!x.isleaf) //在内节点找到关键字
{
BPlusNode child;
ReadBPlusNode(x.Pointer[i], child);
if (child.isleaf) //如果孩子是叶节点
{
if (child.nkey > MAX_KEY / 2) //情况A
{ //1、找到关键字,当前节点是内节点,孩子是叶子节点,孩子节点半满
//直接删除
x.key[i] = child.key[child.nkey - 2];
child.nkey--;
WriteBPlusNode(current, x);
WriteBPlusNode(x.Pointer[i], child);
//删除完就return了
return;
}
else //否则孩子节点的关键字数量不过半
{
if (i > 0) //有左兄弟节点
{
BPlusNode lbchild;
ReadBPlusNode(x.Pointer[i - 1], lbchild);
//2、找到关键字,当前节点是内节点,孩子是叶子节点,孩子节点不半满,左孩子半满
//向左孩子借record
if (lbchild.nkey > MAX_KEY / 2) //情况B
{
//右移键值和指针
for (j = child.nkey; j > 0; j--)
{
child.key[j] = child.key[j - 1];
child.Pointer[j] = child.Pointer[j - 1];
}
//下放父节点键值和指针
child.key[0] = x.key[i - 1];
child.Pointer[0] = lbchild.Pointer[lbchild.nkey - 1];
child.nkey++;
lbchild.nkey--;
//更新父节点键值和指针
x.key[i - 1] = lbchild.key[lbchild.nkey - 1];
x.key[i] = child.key[child.nkey - 2];
//写回
WriteBPlusNode(current, x);
WriteBPlusNode(x.Pointer[i - 1], lbchild);
WriteBPlusNode(x.Pointer[i], child);
}
else //情况C
{
//3、找到关键字,当前节点是内节点,孩子是叶子节点,孩子节点不半满,左孩子也不半满
//向左孩子合并,child页加入freelist
//拷贝
for (j = 0; j < child.nkey; j++)
{
lbchild.key[lbchild.nkey + j] = child.key[j];
lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j];
}
lbchild.nkey += child.nkey;
lbchild.Pointer[MAX_KEY] = child.Pointer[MAX_KEY];
//更新当前内节点
for (j = i - 1; j < x.nkey - 1; j++)
{
x.key[j] = x.key[j + 1];
x.Pointer[j + 1] = x.Pointer[j + 2];
}
x.nkey--;
//i-1指向新的lbchild右端(末端)