删除比插入复杂不少,但深入分析,发现它的关键形状和插入极其类似,记住这个形状!
case1,即sibling 结点为红色,不为黑色,这时需要对其parents结点作一次左旋操作,则此时sibling 结点必为黑色;以下的case2 case3 case4中sibling 结点均为黑色
case2,sibling 结点的两个子结点均为黑色,此时置sibling 结点为红色,然后再以parent 结点为基准重新循环
case3, 互换sibling 及其左子结点颜色(左子结点为红色),然后对sibling 结点作一次右旋操作即转换为case4
case4, 互换sibling 及其父结点颜色,然后对parents结点作一次左旋操作
以下代码完全取自 Linux 内核,刚开始觉得很多地方可以优化,最后发现它是为性能做了最大的优化!
static void rb_delete_color(struct rb_node *node, struct rb_node *parent, struct rb_root *root)
{
struct rb_node *sib;
while ((node == NULL || node->color == RB_BLACK) && node != root->rb_node) {
if (parent->left == node) {
/* case 1, sib is red */
sib = parent->right;
if (sib->color == RB_RED) {
parent->color = RB_RED;
sib->color = RB_BLACK;
__rb_rotate_left(parent, root);
sib = parent->right;
/* goto case 2 */
}
/* case 2, sib is already black, all children of sib is black */
if ((sib->left == NULL || sib->left->color == RB_BLACK)
&& (sib->right == NULL || sib->right->color == RB_BLACK)) {
sib->color = RB_RED;
node = parent;
parent = node->parent;
if (node == root->rb_node)
root->bh--;
continue;
}
/* case 3, sib is already black, right child is black, left child is red */
if (sib->right == NULL || sib->right->color == RB_BLACK) {
sib->color = RB_RED;
sib->left->color = RB_BLACK;
__rb_rotate_right(sib, root);
sib = parent->right;
/* goto case 4 */
}
/* case 4, sib is already black, right child is red */
sib->color = parent->color;
parent->color = RB_BLACK;
if (sib->right)
sib->right->color = RB_BLACK;
__rb_rotate_left(parent, root);
node = root->rb_node;
}
else {
/* case 1, sib is red */
sib = parent->left;
if (sib->color == RB_RED) {
parent->color = RB_RED;
sib->color = RB_BLACK;
__rb_rotate_right(parent, root);
sib = parent->left;
/* goto case 2 */
}
/* case 2, sib is already black, all children of sib is black */
if ((sib->left == NULL || sib->left->color == RB_BLACK)
&& (sib->right == NULL || sib->right->color == RB_BLACK)) {
sib->color = RB_RED;
node = parent;
parent = node->parent;
if (node == root->rb_node)
root->bh--;
continue;
}
/* case 3, sib is already black, left child is black, right child is red */
if (sib->left == NULL || sib->left->color == RB_BLACK) {
sib->color = RB_RED;
sib->right->color = RB_BLACK;
__rb_rotate_left(sib, root);
sib = parent->left;
/* goto case 4 */
}
/* case 4, sib is already black, left child is red */
sib->color = parent->color;
parent->color = RB_BLACK;
if (sib->left)
sib->left->color = RB_BLACK;
__rb_rotate_right(parent, root);
node = root->rb_node;
}
}
if (node) {
node->color = RB_BLACK;
}
}
static void rb_delete(struct rb_node *node, struct rb_root *root)
{
int color;
struct rb_node *child, *parent;
if (node->left == NULL)
child = node->right;
else if (node->right == NULL)
child = node->left;
else { /* use successor instead of node */
struct rb_node *old = node, *left;
node = node->right;
while ((left = node->left) != NULL)
node = left;
child = node->right;
parent = node->parent;
color = node->color;
if (child)
child->parent = parent;
if (old == parent) {/* successor is just the right child of node */
parent->right = child;
parent = node;
}
else
parent->left = child;
/* update successor as old node */
node->parent = old->parent;
node->color = old->color;
node->right = old->right;
node->left = old->left;
if (old->parent) {
if (old->parent->left == old)
old->parent->left = node;
else
old->parent->right = node;
}
else
root->rb_node = node;
old->left->parent = node;
if (old->right)
old->right->parent = node;
goto color;
}
/* only used for node is at most one degree node. */
parent = node->parent;
color = node->color;
if (child)
child->parent = parent;
if (parent) {
if (parent->left == node)
parent->left = child;
else
parent->right = child;
}
else
root->rb_node = child;
color:
if (color == RB_BLACK)
rb_delete_color(child, parent, root);
}
对于代码中的若干问题,以下作出说明(个人在上面也困惑良久)
问题1:如果删除结点为叶子结点,则进入rb_delete_color 后,child 为空,parent 的两个子结点均为空,如此判断方向是否会出错?事实上不会出错,因为红黑树的性质会保证。当删除结点为叶子结点时,只有当其为黑色时,才会进入函数rb_delete_color,由红黑树的定义,其parent 结点必定还有一个非空子结点,否则在删除前parent 结点就会违反性质5,即parent 结点的black hight 不一样了,所以这样判断方向不会有问题(在AVL树中就有问题了,所以传入一个方向的参数)
问题2:当parent 为空时,是否会出错?
parent 为空时,则表明删除结点的根结点,child 会成为新的根结点,所以在函数rb_delete_color 中while 判断node 是否为根结点时会直接退出,不会出错
问题3:rb_delete_color 函数中sibling 结点是否会无效?
由于删除了parent 某个子树的一个黑色结点,则其另一棵子树必定有黑色结点,否则就违反性质5了,所以sibling 结点一定存在
177万+

被折叠的 条评论
为什么被折叠?



