该文主要解释红黑树的删除和删除后的再平衡
所有代码已经过编译,编译环境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);
删除再平衡函数
/*删除再平衡函数*/
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节点
}