B树的删除自己弄了好长时间,才有一点点的眉目,特此记下来,以供以后复习:
一、总之两条原则:
与插入情况相对称,除了根节点外(根节点个数不能少于1),B树的关键字数不能少于t-1个,对于简单的删除情况,我们定位到该关键字所在的某个结点中,如果这个节点中关键字个数恰好是t-1,如果直接删除这个关键字,就会违反B树的规则;这时我们可以考虑两种两种处理方案:
A、把这个结点与其相邻结点合并:合并时需要把父节点的一个关键字加进来;
适合的情况:其相邻结点只有t-1个关键字,其父节点至少有t个关键字,否则就会违反B树的规则;
B、从相邻结点借一个关键字过来,借的过程中,需要中转父节点
适合情况:相邻节点至少有t个关键字
二、为了避免类似插入要进行的回溯
解决方案:我们在从树根向下搜索关键字的过程中,凡是遇到途径的节点,如果该节点的关键字数是t-1,则我们要想办法从其它地方弄一个关键字过来,使得该节点的关键字数至少为t;对与根节点,如果根节点只有1个关键字,且两个子女只有t-1个关键字(下面的方法2),就将两子女合并......生成新的根节点(这是B树高度下降的唯一方式),其余情况和普通内结点一样!
弄一个关键字方法:
1、如果相邻结点有的话就从相邻结点通过父节点中转;
2、如果相邻节点都没有的话,说明相邻结点都只有t-1个关键字,就直接与相邻结点合并以满足要求;
三、步骤:
1、首先沿根节点进行处理:访问到根节点,如果根节点只有一个关键字,且其左右子女只有t-1个关键字,则要把两子女合并,根节点的关键字下移,合并后的节点作为B树新的根节点。(这也是B树高度下降的唯一方式);然后在继续确定key所在的子树,删除关键字key;
2、如果访问到的结点是叶子结点(能保证此叶节点一定有t个关键字,删除key后仍然保持B树的性质),则在叶子结点中寻在key值并删除之,否则无key值;
3、如果访问到的结点是内结点X
1)找到关键字key在x中(能保证该结点一定有t个关键字)。假设关键字key在此节点的位置i处,c(i)[x]为其左邻居left,c(i+1)[x]为其右邻居right(两者都存在)
2)如果某一个邻居结点的关键字个数>=t,则找到以该邻居结点为根节点的子树中最小或最大关键字k'(key的后继或者前驱),然后递归的删除k',最后在x中用k'代替key;(这里面的前驱和后继,视情况而定);
3)如果两个邻居节点的关键字个数都== t-1,则将这两个邻居合并为一个新节点y,然后关键字key下移到合并后的节点y中,再递归删除y中的key;
4、如果关键字不在x中,则key必在以c(i)[x]为根的某个子树中(x结点至少有t个关键字),记current = c(i)[x]
1)如果current结点的的关键字个数>= t,下降至current,递归寻找key然后删除之;
2)current结点的关键字个数 == t-1,找到该节点的邻居结点left == c(i-1)[x](i==1时不存在), y == c(i+1)[x] (i== x->keyNum +1 时不存在),这时就有两种情况:
a、此节点的邻居(left、right)中有一个结点至少有有t个关键字,可从通过邻居中转借父节点一个关键字;
b、此节点的邻居(left、right)都只有t-1个关键字,只能将此节点和其中一个结点合并,并下移父节点x的一个关键字(因为x结点至少有t个关键字,少一个关键字否也能保证B树的性质),接着下移至current,递归寻找key然后删除之;
四、给图进行解答:
4.1、
4.2 现在操作都转移到BTree_Delete_NonOne(x, key)函数中,进入这个函数的前提是x结点的关键字个数必>= t;对于此函数我们又要另外分几种情况进行谈论:
4.2.1、对情况A进行讨论:
4.2.2、对情况B进行讨论:
我在写程序的时候,差点忽略了以下重要的一点:
最后一种情况BTree_MergeNode(x, i-1, left, current)执行后,记住以current指向的结点已经被销毁,这个时候做个操作 : current = left ,然后在对current所指向的结点递归调用BTree_Delete_NonOne(current, key);
5、总结
以上便是B树删除一个结点的总过程,在这个过程中我们:
首先讨论了根节点的特殊情况,并且它也是B树高度下降的唯一途径;
其实讨论了BTree_Delete_NonOne(x, key)函数,对其两种大情况进行了讨论,同时对第二种大情况又分了两种case进行了讨论,并且这两种case中又分了好多个case;在这些不同的case中我们用到了:
1)BTree_MergeNode(x, i, y, z) 函数:将x结点的两个相邻的子女y、z进行合并,同时适当改变x结点;
2)BTree_Shift_RightChild(x, i, left, current)函数:将x结点i处的关键字移动到current第一个位置处,将左邻居最后一个关键字移动到x结点i处(还要做其余指针的处理);
3)BTree_Shift_LeftChild(x, i, current, right)函数:通过x结点给current结点增加一个关键字;
4)BTree_Successor(x)函数:寻找B树中最小的关键字(用在程序里面就是寻找某个关键字的后继),x是B树的根节点;
5)BTree_Precursor(x)函数:寻找B树中最大的关键字(用在程序里面就是寻找某个关键字的前驱),x是B树的根节点;
等过几天我再写一篇blog将这些操作的伪代码附上,并贴上自己写的程序,加油加油加油!!!!!!
最后,经过我的理解,B树的删除,不管是key关键字是在内结点还是在叶结点,都是在“叶节点”进行删除:
1)key关键字在叶节点:略过
2)key关键字在内结点:
case1:找到其前驱或者后继(这些前驱和后继必在叶节点上),然后递归删除这些前驱和后继,最后在用前驱和后继代替key所在的位置,这些操作其实可以看做在“叶节点上”删除key;
我将在随后几天的时间里写成这些函数的伪代码,同时将自己的程序附上;
case2:将其左右邻居合并成一个结点y,key关键字下降到结点y中,然后在调用BTree_Delete_NonOne(y, key),返回到原来的问题:y是叶节点,就在叶节点中删除key,y不是叶节点,又返回到2)这种情况;
3)key关键字在不在内结点,key关键字必在以该内结点的某个子女为根节点的子树上:
case1:该子女结点关键字个数>=t,调用BTree_Delete_NonOne(子女, key),递归寻找key并删除,又回到原来的大问题;
case2:该子女结点关键字个数== t-1,从其关键字结点个数>=t的邻居结点借一个关键字过来,否则与其邻居结点合并,在调用BTree_Delete_NonOne(子女, key),递归寻找key并删除,又回到原来的大问题;
欢迎大家指出错误!
参考文献:
《算法导论》第18章 B树
http://blog.csdn.net/swordmanwk/article/details/6549480
http://blog.chinaunix.net/uid-20196318-id-3030529.html