B树的删除

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

 

posted @ 2014-04-21 14:12  Pacific-hong  阅读(3114)  评论(0编辑  收藏  举报