【学习笔记】红黑树的实现(二):删除算法

来源:互联网 发布:淘宝的营销中心在哪找 编辑:程序博客网 时间:2024/05/17 08:51

该文主要解释红黑树的删除和删除后的再平衡

所有代码已经过编译,编译环境ubuntu12.04LTS GCC 4.6.3

完整的红黑树代码已上传GitHub:https://github.com/xusongqi/Data_Structures

————————————————————————————————————————————

5月6日上午总算把删除算法弄得差不多了......

————————————————————————————————————————————

昨天整理了自己红黑树中插入操作的学习,今天把删除操作给整理了~

红黑树里插入的一般情况有三种,删除的一般情况却有四种,所以基本上来讲,插入操作比删除操作简单一点。

再写一遍红黑树的五个基本性质

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点,即空结点(NIL)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

删除操作和AVL树里面的删除原理基本差不多,不过也存在几个比较蛋疼的地方,下面会写到。


删除函数

删除函数里面,除了删除叶子节点之外,有三种情况:

1)待删结点有两个孩子

2)待删结点只有左孩子(其实可以把叶节点的情况放在这里)

3)待删结点只有右孩子

其中主要注意一点:删除之后,如果删掉的节点是黑的,那么传入删除再平衡函数(Delete_Rebalance(child, parent, root))的第一个参数是【实际删掉节点】的孩子节点。

举个例子:如果待删结点有两个孩子,那么会将该节点右枝中的最小节点(递归寻找左孩子before该节点为NULL)替换掉该待删结点,那么child就是该最小节点的右孩子(因为左孩子为空了)。

发一个流程图


第二个和第三个情况比较简单,因为待删结点的最多只有一个非空的孩子,只需要将这个孩子与待删结点的父节点连起来就OK了。

说下情况一:

1.将待删结点的old右枝中的最小值设为node(具体是:找到待删结点的右孩子,在右孩子中不断递归寻找当前节点的非空左孩子),

然后将child等于node的右孩子,将parent等于node的父节点。


然后断开node与parent以及child的连接,将child与parent建立连接。


最后如果node是黑色的,将child和parent传入函数Delete_Rebalance(child, parent, index);


删除再平衡函数

上面说删除操作要比插入操作难一点,其实就是难在再平衡上面。
首先是两种特殊情况:
1)当前节点是红色节点:直接染黑,结束。
2)当前节点是根结点:置黑,结束。
再平衡函数里面有四种一般的情况,分别是(当前节点node = child):
【以下四种情况为当前节点为父节点的左孩子情况下,若为父节点的右孩子时情况为镜面对称】
1)当前节点的兄弟节点为红色
2)当前节点的兄弟节点为黑色,两个子节点也为黑色
3)当前节点的兄弟节点为黑色,左孩子为红色,右子孩子为黑色
4)当前节点的兄弟节点为黑色,左孩子颜色任意,右孩子为红色
流程图如下:


看起来…的确比较复杂……
其实核心思想概括起来就是一句话:当前节点前面少了一个黑节点,我们要给它前面再加一个黑节点,并且不影响其它节点路径上的黑节点数量。
下面简单说一下四种情况~
1)当前节点的兄弟节点为红色
【情况分析】:因为兄弟节点为红色,所以父节点为黑色,它的两个孩子节点也是黑色。
【解决办法】:将父节点染红,兄弟节点染黑,然后左旋父节点。继续算法……
图例【以下图片引自:结构之法,算法之道】


2)当前节点的兄弟节点为黑色,两个子节点也为黑色
【解决办法】:将兄弟节点染红,然后根据父节点的情况进行判断
1.父节点为黑色:将父节点成为新的当前节点,继续算法(未改变性质五)
2.父节点为红色直接将父节点染黑变为平衡结束算法
图例【以下图片引自:结构之法,算法之道


3)当前节点的兄弟节点为黑色,左孩子为红色,右子孩子为黑色
【解决办法】:将左孩子染黑,兄弟节点染红,右旋兄弟节点。继续算法(直接跳转情况四)……
图例【以下图片引自:结构之法,算法之道


4)当前节点的兄弟节点为黑色,左孩子颜色任意,右孩子为红色
【解决办法】兄弟节点染为父节点的颜色,右孩子染黑,父节点染黑,然后左旋父节点。树平衡,结束算法。
图例【以下图片引自:结构之法,算法之道


以上就是两种特殊情况与四种一般情况,下面是代码:
/*删除再平衡函数*/static RB_Tree RB_Delete_Rebalance(RB_Tree node, RB_Tree parent, RB_Tree index){RB_Tree brother;//节点的兄弟/*开始循环调整,以下四种情况,当前节点(node)均为黑色*/while((!node || node->color == BLACK) && node != index){/*当前节点为父节点的左子*/if(parent->leftChild == node){brother = parent->rightChild;/*情况一:兄弟节点为红色 *解决办法:父节点变红,兄弟节点变黑,左旋父节点 * */if(brother->color == RED){parent->color = RED;brother->color = BLACK;index = RB_Rotate_Left(parent, index);}else//以下是brother颜色为黑的三种情况{/*情况二:兄弟节点为黑,兄弟节点的两个子节点也为黑*解决办法:先将兄弟节点变红,然后根据父节点:1.若父节点为黑,父节点成为当前节点,继续算法2.若父节点为红,将父节点变黑。树平衡,结束算法 * */if( (!brother->leftChild || brother->leftChild->color == BLACK) &&(!brother->rightChild || brother->rightChild->color == BLACK) ){brother->color = RED;//若父节点为红,变为黑,结束算法if(parent->color == RED){parent->color = BLACK;break;}node = parent;//父节点成为新的当前节点(父节点原即为黑)parent = node->parent;continue;//重新进入算法循环}/*情况三:兄弟节点为黑,兄弟节点的左孩子为红,右孩子为黑 *解决办法:将兄弟节点染红,兄弟节点的左孩子染黑,右旋兄弟节点 * */if( (brother->leftChild && brother->leftChild->color == RED) && (!brother->rightChild || brother->rightChild->color == BLACK)){brother->color = RED;brother->leftChild->color = BLACK;index = RB_Rotate_Right(brother, index);}/*情况四:兄弟节点为黑,兄弟节点的右孩子为红,左孩子颜色任意 *解决办法:兄弟节点染成父节点的颜色,父节点染黑,兄弟节点的右孩子染黑,左旋父节点。树平衡,算法结束 * */brother->color = parent->color;parent->color = BLACK;brother->rightChild->color = BLACK;//此时该节点原本颜色一定为红色index = RB_Rotate_Left(parent, index);node = index;}//[else:bro为黑]结束}//[if:node为父节点左孩子]结束/*当前节点为父节点右孩子*/else{brother = parent->leftChild;/*情况一:兄弟节点为红 *解决办法:父节点染红,兄弟节点染黑,右旋父节点 * */if(brother->color == RED){parent->color = RED;brother->color = BLACK;index = RB_Rotate_Right(parent, index);}/*以下三种情况,兄弟节点均为黑色*/else{/*情况二:兄弟节点为黑,兄弟节点的两个字节点也为黑*解决办法:先将兄弟节点变红,然后根据父节点:1.若父节点为黑,父节点成为当前节点,继续算法2.若父节点为红,将父节点变黑。树平衡,结束算法 * */if( (!brother->leftChild ||brother->leftChild->color == BLACK) && (!brother->rightChild || brother->rightChild->color == BLACK)){brother->color = RED;//若父节点为红,染黑,树即平衡。if(parent->color == RED){parent->color = BLACK;break;}node = parent;//父节点成为新的当前节点(父节点原来即黑色)parent = node->parent;continue;//重新进入算法循环}/*情况三:兄弟节点为黑,兄弟节点的右孩子为红,左孩子为黑 *解决办法:兄弟节点变红,兄弟节点的右孩子变黑,左旋兄弟节点 * */if( (brother->rightChild && brother->rightChild->color == RED) &&(!brother->leftChild || brother->leftChild->color == BLACK)){brother->color = RED;brother->rightChild->color = BLACK;index = RB_Rotate_Left(brother, index);}/*情况四:兄弟节点为黑,兄弟节点的左孩子为红,右孩子颜色任意 *解决办法:兄弟节点染成父节点颜色,父节点染黑,兄弟节点的左孩子染黑,右旋父节点 * */brother->color = parent->color;parent->color = BLACK;brother->color = BLACK;index = RB_Rotate_Right(parent, index);}//[else:兄弟节点为黑]结束}//[else:node为父节点右孩子]结束}//[while:删除再平衡算法]结束/*将root点的颜色置为黑色*/if(node){node->color = BLACK;}return index;//返回root节点}

参考资料

你透彻了解红黑树—结构之法,算法之道

红黑树的C实现完整源码—结构之法,算法之道

红黑树删除—spch2008 

还有一片资料对我理解(一般情况二)的帮助很大,但是找不到了.....


0 0
原创粉丝点击