摘要:删除操作与插入基本相似,但是比较复杂(算法部分取自算法导论)。
(0)因为删除操作可以从任意内部节点删除,这导致必须安排它的子女;
(1)除了根节点,一定要保证删除不会使得该节点的元素个数少于t-1个.
(3)因此在递归降至某个节点之前,一定要保证它的父节点有t个子女.
下面分几种情况讨论B树删除的操作:
(1)如果关键字k在节点x中,且x是个叶子节点,则删除x;
(2)如果关键字k在节点x中且x是个内节点,则有如下操作;
a)如果节点x中前于k的子节点y包含至少k个关键字,则找出以y为根的子树前驱k’,递归的删除k’,并在x中用k’取代k.
b)对称的,如果节点x中位于k之后的子节点z包含至少t个关键字,则可以找出k在以z为根的子树中的后继k’,递归的删除k’,并用k’代替k。
c)如果关键字k的前后子节点都只有t-1个关键字,就将y和z合并到y,这使得x失去指向k和z的指针,释放z并将k从y中递归的删除z.
3)如果k不在内节点x中,则确定必包含k的正确子树的根ci[x],如果ci[x]只有t-1个关键字,执行步骤3(a/b),保证我们降至一个有t个关键字的节点.
a)如果ci[x]只有t-1个关键字,但他的一个兄弟包含t个关键字,则x中的某一个关键字降至ci[x],将ci[x]相邻兄弟的某一关键字上升到x。
b)如果ci[x]与相邻兄弟都只有t-1个关键字,就将ci[x]与一个兄弟合并,即将x的一个关键字降至新合并的节点(这里会让父节点减少,因此必须保证每一个节点被到达之前都有t的关键字,否则就需要回溯).
void combine(Position T,Position T2,int x)
{
//把关键字x,T2中所有的关键字合并入T
int max = maxIndex(T);
int j = 0;
T->data[max + 1] = x;//合并关键字与子节点
for(int i = max + 2;i<=2*DU-2;i++)
T->data[i] = T2->data[j++];
j = 0;
for(int i = max + 2;i<=2*DU-1;i++)
T->child[i] = T2->child[j++];
}
int FindPrevious(Position T)//前继
{
int max = maxIndex(T);
while (!T->isLeaf)
{
T = T->child[max+1];
max = maxIndex(T);
}
return T->data[max];
}
int FindNext(Position T)//后继
{
while(!T->isLeaf)
T = T->child[0];
return T->data[0];
}
void DeleteBtree(Position *T,int x)
{
int k,indexmax,j;
int temp,max;
Position C,Temp;//用来保存正确的子节点
if((*T) == NULL)
//没有删除成功
return;
int i = 0;
max = maxIndex(*T);
while(i<= max&&x>(*T)->data[i])
i++;
if (i<=2*DU-2&&x == (*T)->data[i])
{
if ((*T)->isLeaf == 1)
//x在叶子节点上
shiftLeft(*T,i,1);//左移一位
else
//x在内节点
{
if( pageSize((*T)->child[i]) >=DU )
{
k = FindPrevious((*T)->child[i]);
(*T)->data[i] = k;
DeleteBtree( &(*T)->child[i],k);
}
else if ( pageSize((*T)->child[i+1]) >=DU )
{
indexmax = maxIndex((*T)->child[i+1]);
k = FindNext((*T)->child[i+1]);
(*T)->data[i] = k;
DeleteBtree( &(*T)->child[i+1],k);
}
else
{
//合并
combine((*T)->child[i],(*T)->child[i+1],x);
free((*T)->child[i+1]);
for( j = i;j<=2*DU-3;j++)
{
(*T)->data[j] = (*T)->data[j+1];
(*T)->child[j+1] = (*T)->child[j+2];
}
(*T)->data[j] = 0;
(*T)->child[j+1] = NULL;//以上为对T的关键字和子节点进行移动
DeleteBtree(&(*T)->child[i],x);//在合并的新节点递归的删除关键字x
}
}//x 在内部节点
}//x在当前节点
else if ((*T)->isLeaf == 1)
puts("not found");
else
{
//继续向下找
C = (*T)->child[i];
if(pageSize(C) == DU-1)
{
max = maxIndex(*T)+1;
temp = -1;
//执行下列步骤保证降至一个包含t个关键字的节点
if(i == 0&&(pageSize((*T)->child[i+1]) >=DU))
temp = 0;//右兄弟
else if ( i == max&&(pageSize((*T)->child[i-1]) >=DU))
temp = 1;//左兄弟
else
{
if(pageSize((*T)->child[i+1]) >=DU )
temp = 0;
else if (pageSize((*T)->child[i-1]) >=DU)
temp = 1;
}
//将*T中的合适关键字降入C中,将C的左/右兄弟的合适关键字放入T中
if(temp == 0)//右兄弟
{
C->data[maxIndex(C)+1] = (*T)->data[0];//将T的最小关键字给C作为C的最大关键字
C->child[maxIndex(C)+2] = (*T)->child[i+1]->child[0];//将右兄弟的最左子节点合并到C
shiftLeft(*T,0,1);
(*T)->data[maxIndex(*T)] = (*T)->child[i+1]->data[0];//将右兄弟最小关键字给T的最大
shiftLeft((*T)->child[i+1],0,1);
}
else if (temp == 1)//左兄弟
{
max = maxIndex((*T)->child[i-1]);
shiftRight(C,0);
C->data[0] = (*T)->data[maxIndex(*T)];
C->child[0] = (*T)->child[i-1]->child[max+1];
(*T)->data[maxIndex(*T)] = (*T)->child[i-1]->data[max];//将左兄弟最大关键字给T
shiftLeft( (*T)->child[i-1],max,1);//删除左兄弟的最大关键字
}
else
//合并C与左/右兄弟
{
if (i== 0)
//合并右兄弟
{
combine(C,(*T)->child[i+1],(*T)->data[i]);
free((*T)->child[i+1]);
for( j = i;j<=2*DU-3;j++)
{
(*T)->data[j] = (*T)->data[j+1];
(*T)->child[j+1] = (*T)->child[j+2];
}
(*T)->data[i] = 0;
(*T)->child[j] = NULL;//以上为对T的关键字和子节点进行移动
}
else
{
//合并左兄弟
combine((*T)->child[i-1],C,(*T)->data[i-1]);
free(C);
C = (*T)->child[i-1];
for( j = i;j<=2*DU-3;j++)
{
(*T)->data[j] = (*T)->data[j+1];
(*T)->child[j] = (*T)->child[j+1];
}
(*T)->data[i-1] = 0;
(*T)->child[j] = NULL;//以上为对T的关键字和子节点进行移动
(*T)->child[j+1] = NULL;
}
}
}
// C满足至少t个节点,递归删除x
DeleteBtree( &C,x);
}
//检测是否为空,否则删除空根
if(pageSize(*T) == 0)
{
if ((*T)->parent == NULL)
{
Temp = *T;
(*T) = (*T)->child[0];
(*T)->parent = NULL;
free(Temp);
}
}
}