要搞清楚红黑树的删除,一定要结合红黑树的5个性质:
1. 每个节点或是红的或是黑的
2. 根节点为黑色
3. 外部节点是黑色的
4. 如果一个结点是红色的,那么它的两个儿子都是黑色的 (红红不能相连)
5. 对每个结点的,从该节点出发到外部结点的所有路径上包含相同个数的黑色结点(黑高相同)
红黑树的删除策略:
如果待删除的结点y 有两个孩子节点,那么找该树中序遍历中结点y 的直接前驱或者后继,用前驱或者后继的值替换y的值,然后我们要删除的结点就变成了y 的前驱或者后继。而前驱或者后继要么是叶子结点,要么是单分支结点,无非这两种情况!
也就是说,我们将任意结点的删除都转化为叶子或者单分支结点的删除!
接下来说明具体的删除步骤:
1. 删除的结点Y 是个红色的节点,那么我们的处理就是将Y 直接删除,理由如下:
(1)树中各个节点的黑高没有变化
(2)删除后满足性质4,因为不会出现 红红相连 的情况
(3)删除的不可能是根节点,因为根节点是黑色的
2. 删除的结点Y 是个黑色的结点,那么我们需要特殊处理:做法就是直接将Y 删除,然后我们将Y 的黑色作为额外属性给予Y 的孩子结点 X(这么做使得红黑树在逻辑上的"黑高"还是没有变化的);但是这个做法使得结点X 有了双重颜色,这不符合性质一,分为以下两类讨论加解决:
2.1. X原来的颜色为红色
做法:直接将X的颜色改为黑色;做完后就满足了性质一 和 性质五,同时也是绝对满足其他性质,此时调整结束!
2.2. X原来的颜色为黑色
结合上述,此时的 X 肯定是一个有双重黑色性质的结点!这个特点在接下来的调整中异常重要,而且我们在接下来的调整过程中一定要保证树的黑高不变!
2.2.1 X的兄弟节点为红色(此模式也称 case1)
此模式下,我们结合性质四(红红不能相连)可以得到该模式下的红黑树图示一定为:
该模式的变化法则如图所示:
该模式的变化有着两个特点: 1. X的兄弟结点变化后一定为黑色; 2. 该变化未解决X为双黑节点的问题
2.2.2. X的兄弟结点为黑色
case 2: X的兄弟为黑色,X兄弟的两个孩子都是黑色的
此模式下的红黑树图示如下:
此模式下的变换规则是将X 及其兄弟结点的一个黑色性质上移,如图所示:
此变换的特点:将黑色属性上移,不旋转,并将X上移至其父节点处;然后进行以新的X继续进行调整
而此变换可能导致我们每次循环都是以case2的结束状态作为下一次循环的开始状态。即一直在做 case2或者case1+case2;直到X==RBTree.root就不必循环下去,调整结束!因为从跟到每个叶的黑高都相同
但此模式未处理X 额外的黑色性质,却有可能退出调整的循环!!!!!
case3: X的兄弟为黑色,X兄弟的内侧结点为红色,X兄弟的外侧孩子为黑色
该模式的说明:当不满足case2,即X的兄弟为黑色,X兄弟的两个孩子不全都是黑色的时;X兄弟的两 个孩子可能为双红或者一红一黑:即“内红外黑”,“内黑外红”,“双红色”,如图所示:
但是case3只关注“内红外黑”情况,处理方式如下:
该模式的特点:未处理X的双黑性质,但是将X兄弟节点的外孩子变成了红色
case 4: X的兄弟为黑色,X兄弟的外侧孩子为红色
该模式的特点:如果没有命中case2,则有以下几种可能:
图中的情况三一定会被case3处理,而case3的结果就使得 X兄弟的外侧孩子为红色,所以上述的全部情况 最终都会变成 X兄弟的外侧孩子为红色,即case4;所以说case4的模式图示为:
而该模式的变化规则是:X的兄弟节点继承 X父节点的颜色,而X父节点的颜色变为黑色,X兄弟的外孩子变成黑 色,然后以X的父节点进行左旋(X在父节点哪边就往哪边旋),结果如下:
模式特点:未变化之前X兄弟节点的外侧孩子为红色,我们将其改为黑色,这个时候树中多出了一个黑色节点, X正好缺一个黑色,只要我们将多出来的这个黑色用来弥补X缺少的,这个 额外黑色 就被消除了,即此时就可以结 束调整,所有的问题都得到解决!!
总结:
case1是case2,3,4的辅助步骤
case2具备退出调整循环的能力
case3的执行结果必定满足case4需要的模式
case4的执行就可以立马结束整个调整的过程
整个调整过程最多只需要3次旋转,3次旋转分别出现在:case1,case3,case4
调整函数的伪代码:
调整函数的代码:
void RB_DELETE_FIXUP(RBTree &myt, rb_node *_X)
{
while (_X != myt.head->parent && _X->color == BLACK) //_X非跟结点 且 _X颜色为黑时才会执行循环体
{
if (_X == _X->parent->leftchild)
{
rb_node *_Y = _X->parent->rightchild;
if (_Y->color == RED) //case 1
{
_Y->color = BLACK;
_X->parent->color = RED;
RotateLeft(myt.head, _X->parent);
_Y = _X->parent->rightchild;
}
if (_Y->leftchild->color == BLACK && _Y->rightchild->color == BLACK) //case 2
{
_Y->color = RED;
_X = _X->parent;
}
else if (_Y->rightchild->color == BLACK)
{
_Y->leftchild->color = BLACK; //case 3
_Y->color = RED;
RotateRight(myt.head, _Y);
//_Y = _X->parent->rightchild;
}
else
{
_Y->rightchild->color = BLACK; //case 4
_Y->color = _X->parent->color;
_X->parent->color = BLACK;
RotateLeft(myt.head, _X->parent);
_X = myt.head->parent; //_X为根节点,意味着不必再循环了
}
}
else //_X现在在其父节点的右边
{
rb_node *_Y = _X->parent->leftchild;
if (_Y->color == RED)
{
_Y->color = BLACK;
_X->parent->color = RED;
RotateRight(myt.head, _X->parent);
_Y = _X->parent->leftchild;
}
if (_Y->leftchild->color == BLACK && _Y->rightchild->color == BLACK)
{
_Y->color = RED;
_X = _X->parent;
}
else if (_Y->leftchild->color == BLACK)
{
_Y->color = RED;
_Y->rightchild->color = BLACK;
RotateLeft(myt.head, _Y);
//_Y = _X->parent->leftchild;
}
else //_Y->leftchild->color == RED
{
_Y->leftchild->color = BLACK;
_Y->color = _X->parent->color;
_X->parent->color = BLACK;
RotateRight(myt.head, _X->parent);
_X = myt.head->parent;
}
}
}
_X->color = BLACK;
}
上述代码是对伪代码的翻译,具体请自行实现符合自己结构的代码,但逻辑框架可用