B树的代码改改写写,码了很多次 每次都会发现新的问题 也有新的收获
最终终于在2016年的第一天解决了问题
对于高阶(三/四阶以上)的B树其实代码写起来并不麻烦 最麻烦的是3阶B树,也就是2-3树 特别是删除这一块
举个例子 如下图的2-3树 先删除2 得到右图的2-3树
接下来需要调整第二层的结点 用调整2(最下层结点)的方法来调整中间结点是不行的 操作难度也大
所以这里对中间节点的调整应该是这样的
若有左兄弟,则和左兄弟合并。若合并之后的节点关键字数目过大 则分裂这个结点 否则向上递归调整,直至根结点;若没有左兄弟 则对右兄弟做同样的操作。 具体如图:
可见 44 89 98关键字多了 此时就要分裂:
分裂之后向上递归调整 到达根结点 根结点只有一个关键字符合B树特性 故调整结束
完整代码实现如下:
void split(BTree &q, int s, BTree &ap)
{
int i, j, n = q->keynum;
ap = (BTNode *)malloc(sizeof(BTNode)); //生成新节点
ap->ptr[0] = q->ptr[s];
for (i = s + 1, j = 1; i <= n; i++, j++) {
ap->key[j] = q->key[i];
ap->ptr[j] = q->ptr[i];
}
ap->keynum = n - s;
ap->parent = q->parent;
for (i = 0; i <= ap->keynum; i++) if (ap->ptr[i] != NULL) ap->ptr[i]->parent = ap;
q->keynum = s - 1; //保留左侧
}
void successor(BTree &p, int i)
{
BTree q = p->ptr[i - 1];
while (q->ptr[q->keynum] != NULL) q = q->ptr[q->keynum]; //取左边最大代替
p->key[i] = q->key[q->keynum]; //最底层最大和p的第q->keynum个交换
p = q; //替代之后令p指向被替代的位置
}
//p在parent的位置
int findLocation(BTNode *node)
{
BTree parent = node->parent;
if (parent == NULL) return -1;
int loc = 0;
for (loc = 0; loc <= parent->keynum; loc++) {
if (parent->ptr[loc] == node) break; //有点不科学
}
return loc;
}
void removeKey(BTree &p, int i)
{
//只对最下层结点调用
int n = p->keynum--;
for (int j = i; j < n; j++) p->key[j] = p->key[j + 1];
}
/*将B树t的第i个关键字K[i]连同第i+1棵子树A[i]合并到第i棵子树A[i-1]中*/
void combineBTNode(BTree &t, int i)
{
KeyType parentKey;
BTree lNode; //左节点
BTree rNode; //右节点
lNode = t->ptr[i - 1];
rNode = t->ptr[i];
parentKey = t->key[i];
++lNode->keynum;
lNode->key[lNode->keynum] = parentKey; //先添加双亲节点上的一个关键字
lNode->ptr[lNode->keynum] = rNode->ptr[0];
if (rNode->ptr[0] != NULL) rNode->ptr[0]->parent = lNode;
for (int k = 1; k <= rNode->keynum; k++) { //添加rNode的所有关键字
++lNode->keynum;
lNode->key[lNode->keynum] = rNode->key[k];
lNode->ptr[lNode->keynum] = rNode->ptr[k]; //复制指针 adjustTopNode用到
if (rNode->ptr[k] != NULL) rNode->ptr[k]->parent = lNode; //修改双亲结点
}
//移除t的关键字Ki和指向p的指针Ai
for (int j = i; j < t->keynum; j++) {
t->key[j] = t->key[j + 1];
t->ptr[j] = t->ptr[j + 1];
}
t->keynum--; //双亲结点数-1
}
//调整中间结点p
void adjustTopNode(BTree &root, BTree &p)
{
//上层节点调整策略:合并
BTree parent = p->parent;
int pos; //合并之后的位置
if (parent == NULL) {
if (p->keynum == 0) {
root = root->ptr[0];
root->parent = NULL;
}
return;
}
int i = findLocation(p);
int n = parent->keynum;
if (i + 1 <= n) pos = i + 1;
else pos = i;
combineBTNode(parent, pos);
//检查合并点
BTree q = parent->ptr[pos - 1];
if (q->keynum >= M) {
BTree ap;
int s = (q->keynum + 1) / 2;
KeyType midKey = q->key[s];
split(q, s, ap);
for (int j = parent->keynum; j > pos; j--) {
parent->ptr[j] = parent->ptr[j - 1];
parent->key[j] = parent->key[j - 1];
}
parent->ptr[pos] = ap;
parent->key[pos] = midKey;
parent->keynum++;
}
//检查双亲结点
if (parent->keynum < (M + 1) / 2 - 1) adjustTopNode(root, parent); //递归调整
}
//调整最下层结点p
void adjustLowestNode(BTree &root, BTree &p)
{
BTree parent = p->parent; //parent非空
if (parent == NULL) return;
int i = findLocation(p);
int n = parent->keynum;
BTree brother;
KeyType parentKey;
/*右兄弟存在且关键字数目大于[m/2]-1*/
if (i + 1 <= n && parent->ptr[i + 1]->keynum > (M + 1) / 2 - 1) {
brother = parent->ptr[i + 1]; //右兄弟
parentKey = parent->key[i + 1];
parent->key[i + 1] = brother->key[1]; //第一个(最小)关键字上移
p->key[++p->keynum] = parentKey; //parentKey下移 肯定是最大 指针本来就为NULL无需调整
removeKey(brother, 1); //移去brother的最小关键字
}
/*左兄弟存在且关键字数目大于[m/2]-1*/
else if (i - 1 >= 0 && parent->ptr[i - 1]->keynum > (M + 1) / 2 - 1) {
brother = parent->ptr[i - 1]; //左兄弟
parentKey = parent->key[i];
parent->key[i] = brother->key[brother->keynum]; //最后一个(最大)关键字上移
for (int j = p->keynum; j > 0; j--) p->key[j + 1] = p->key[j];
p->key[1] = parentKey;
++p->keynum;
removeKey(brother, brother->keynum);
}
/*没有富余结点 进行合并*/
else {
if (i + 1 <= n) combineBTNode(parent, i + 1); //合并右兄弟
else combineBTNode(parent, i); //合并左兄弟
//检查双亲结点
if (parent->keynum < (M + 1) / 2 - 1) adjustTopNode(root, parent);
}
}
/*删除B树root的子树p的关键字K[i]*/
void deleteKey(BTree &root, BTree &p, int i)
{
if (p->ptr[i - 1] != NULL) { //not last level
successor(p, i);
deleteKey(root, p, p->keynum);
}
else {
removeKey(p, i);
if (p->keynum < (M + 1) / 2 - 1) //删除之后关键字个数小于(m-1)/2
adjustLowestNode(root, p);
}
}
/*删除函数
* @param t B树
* @param key 删除关键字
* @return Status类型数据
* 成功返回OK 失败返回ERROR
* @user ChenJunhan 2015-12
*/
Status deleteBTree(BTree &t, KeyType key)
{
Result r;
r = searchBTree(t, key);
if (r.tag == 0) return ERROR;
else deleteKey(t, r.pt, r.pos);
return OK;
}