【问题标题】:RedBlack tree deletion Algorithms红黑树删除算法
【发布时间】:2021-10-17 08:50:00
【问题描述】:

我正在阅读《算法简介》这本书 在章节红黑树删除部分 他们向我展示了下面的代码

RB-DELETE(T,z)
1 y = z
2 y-original-color = y:color
3 if z.left == T.nil
4       x = z.right
5       RB-T RANSPLANT .T; ´; ´:right/
6 elseif z.right == T:nil
7       x =z.left
8       RB-T RANSPLANT (z,z.left)
9 else y = Minimum(z.right)
10      y-original-color = y.color
11      x = y.right
12      if y.p == z
13          x.p = y
14      else RB-TRANSPLANT (T,y,y.right)
15          y.right = z.right
16          y.right.p = y
17          RB-TRANSPLANT(T,z,y)
18      y.left = z.left
19      y.left:p = y
20      y.color = z.color
21if y-original-color == BLACK
22      RB-DELETE-Fix-Up(T,x)

这是 C++ 中的实现

void RedBlack::transplant(ColoredNode* destination, ColoredNode* source)
{
    if(destination->parent == centinel)
        this->root = source;
    else if(destination->parent->left == destination)
        destination->parent->left = source ;
    else
        destination->parent->right = source;
    source->parent = destination->parent;

}

void RedBlack::DeleteNode(ColoredNode* node)
{
    bool replacementColor = node->black;
    ColoredNode* replacement = node ;
    ColoredNode* repReplacement = node;

    if(node->left == this->centinel && node->right == this->centinel)
    {
        transplant(node , centinel); // IT CHANGES THE CENTINEL PARENT 
    }
    else if(node->left == centinel && node->right != centinel)
    {
        repReplacement = node->right;
        this->transplant(node,node->right);
    }
    else if(node->right == centinel && node->left != centinel)
    {
        repReplacement = node->left ;
        this->transplant(node,node->left);
    }
    else
    {
        replacement = this->successor(node);
        replacementColor = replacement->black;  // save the color of replacement ...
        repReplacement = replacement->right ;
        if(replacement == node->right)
            repReplacement->parent = replacement ;  
            // this line is in case of repRe** == centinel ,centinel->parent == null then the fixup function will fail cause we don't have any path to ancestors ...
        else
        {
            transplant(replacement,repReplacement);
            replacement->right = node->right;
            node->right->parent = replacement;
        }
        transplant(node,replacement);
        replacement->left = node->left;
        node->left->parent = replacement ;
        replacement->black = node->isBlack();
    }
    
    if(replacementColor)    // if the replacement is black ...
        this->DeleteNodeFixUp(repReplacement);

}

删除修正算法也是:

RB-DELETE-FIXUP (T,x)
1 while x != T.root and x.color == BLACK
2       if x == x.p.left
3               w = x.p.right
4               if w.color == RED      // CASE 1
5                   w.color = BLACK
6                   x.p.color = RED
7                   LEFT-ROTATE(T,x.p)
8                   w D x.p.right

9               if w.left.color == BLACK and w.right.color == BLACK // CASE 2
10                  w.color = RED
11                  x = x.p
12              else if w.right.color == BLACK // CASE 3
13                  w.left.color D BLACK
14                  w.color D RED
15                  RIGHT-ROTATE(T,w) 
16                  w = x.p.right
17              w.color = x.p.color  //CASE 4
18              x.p.color = BLACK
19              w.right.color = BLACK
20              LEFT-ROTATE(T, x.p)
21              x = T.root
22      else (same as then clause with “right” and “left” exchanged)
23 x:color D BLACK

我的 C++ 实现是:

void RedBlack::DeleteNodeFixUp(ColoredNode* node)
{
        ColoredNode *parent = node->parent ;
        ColoredNode *sibling = this->centinel ;

        while( node != this->root && node->isBlack())
        {
            
            if(node == parent->left)
            {
                sibling = parent->right ;
                printf("node = %d , parent =%d ,sibling = %d \n",node->data,parent->data,sibling->data);
                if(sibling->isRed())
                {
                    printf("sibling is red\n");
                    parent->black = false;
                    sibling->black = true;
                    node->black = true ;
                    LeftRotation(parent);
                    sibling = parent->right;
                }
                    
                if(sibling->isBlack())
                {
                    // CASE 2 : SIBLING AND ITS CHILDREN ARE BLACK
                    if(sibling->right->isBlack() && sibling->left->isBlack())
                    {
                        sibling->black = false ;
                        node = parent ;
                        parent = node->parent;
                        if(node == parent->left)
                            sibling = parent->right;
                        else    
                            sibling = parent->left;
                        //continue;
                    }
                    else if(sibling->right->isBlack())//CASE 3 : SIBLING IS BLACK AND ITS RIGHT IS BLACK
                    {
                        sibling->black = false;
                        sibling->left->black = true;
                        RightRotation(sibling);
                        sibling = parent->right;
                    }
                }

                //CASE 4 : SIBLING IS BLACK AND SIBLING'S RIGHT IS RED
                sibling->right->black = true;
                sibling->black = false;
                parent->black = true;
                LeftRotation(parent);
                node = this->root;
            }
            else        // node is right child and sibling 
            {
                sibling = parent->left ;
                printf("node = %d , parent =%d ,sibling = %d \n",node->data,parent->data,sibling->data);
                if(sibling->isRed())
                {
                    parent->black = false;
                    sibling->black = true;
                    node->black = true ;
                    RightRotation(parent);
                    sibling = parent->left;
                }

                if(sibling->isBlack())
                {
                    // CASE 2 : SIBLING AND ITS CHILDREN ARE BLACK ...
                    if(sibling->left->isBlack() && sibling->right->isBlack())
                    {
                        sibling->black = false ;
                        node = parent ;
                        parent = node->parent;
                        
                        //continue;
                    }
                    else if(sibling->left->isBlack())//CASE 3 : SIBLING IS BLACK AND ITS LEFT IS BLACK
                    {
                        sibling->right->black = true;
                        sibling->black = false ;
                        RightRotation(sibling);
                        sibling = parent->left ;
                    }
                }

                sibling->left->black = true;
                //CASE 4 : SIBLING IS BLACK AND ITS LEFT IS RED
                sibling->black = false;
                parent->black = true;
                RightRotation(parent);
                node = this->root;
            }
        }

        
        node->black = BLACK_NODE ;
}

但是当我测试所有内容时,我发现修复算法存在错误: 这是我的树

                    N
                              50|B
                                        N
                    35|R
                                        N
                              24|B
                                        N
          23|B
                                        N
                              21|B
                                        N
                    20|R
                                        N
                              19|B
                                        N
15|B
                              N
                    4|B
                                        N
                              2|R
                                        N
          1|B
                              N
                    -4|B
                                        N
                              -30|R
                                        N

当我删除 23 时,结果树是

                              N
                    50|R
                              N
          35|R
                              N
                    24|B
                                                  N
                                        21|B
                                                  N
                              20|R
                                                  N
                                        19|B
                                                  N
15|B
                              N
                    4|B
                                        N
                              2|R
                                        N
          1|B
                              N
                    -4|B
                                        N
                              -30|R
                                        N

如您所见,35 和 50 违反了第三条规则(每个红色节点的两个子节点都必须是黑色)

我认为我必须在 CASE 2 中添加一条 continue 语句 ...

【问题讨论】:

  • 我对这本书一无所知,但要记住的是,伪代码往往会出错,因为它不能被有效地测试。您可能会更好地研究经过测试的实现。我一直很喜欢 Ben Pfaff 的库,它是用 C Web 编写的,这是一种古老的文字编程系统。见adtinfo.org。 PDF 表单既美观又有趣。那里的红黑树有几种变体。
  • 我认为我必须在 CASE 2 中添加一条 continue 语句。 你可以。 CASE 2 以父节点作为要修复的新节点恢复 while 循环。现在有一个新的父母,新的兄弟姐妹,新兄弟姐妹的孩子。

标签: c algorithm data-structures


【解决方案1】:

我不确定这是否是一个“答案”,而是一个建议。

在实现了 RB 树库后,我可以向您保证,删除算法是真正的 PITA。有很多特殊情况,在尝试用任何语言实现之前,您需要了解(在纸上)。不要失礼,但我非常怀疑是否有人愿意在这里免费为您找到您的错误。

也就是说,除非您的目标特别是从头开始实现 RB 树的挑战,否则我强烈建议您改为实现 AVL 树。您会发现算法要简单得多,并且插入和删除之间很容易对称,而 RB 树则不然。而且AVL树和RB树的性能特点一样,何必折磨自己呢?

附:真心希望不要指派学生去实现RB树删除!

附言您可以从跳过列表中获得类似树的性能,与 RB 树或 AVL 树相比,它的实现非常简单。

【讨论】: