C++ red-black 树实现的问题

Problems with C++ red-black tree implementation

我正在尝试制作 RB 树的 C++ 实现。但是当我尝试删除一个元素时——我发现树的一些叶子具有不同的黑色高度值。在调用 remove 函数之前一切正常 - 树插入后的插入和重新平衡工作正常。但是在一次调用 remove 函数之后它就不再平衡了。

常规删除:

void RbTree::remove(int data) {
Node* cur = root;
while (cur != nil && cur->data != data) { // Searching for node with expected key
    if (cur->data < data)
        cur = cur->RIGHT;
    else
        cur = cur->LEFT;
    if (cur == nil) 
        return;
}
removeNode(cur); // Recurrent BST node removing

RbTree::Node* RbTree::removeNode(Node*& cur) {
    if (cur->RIGHT == nil && cur->LEFT == nil) { // No children
        Node* dad = cur->ancestor;
        if (cur == root) { // If current node is root
            cur = root = nil;
        }     else { // Dad now points to nil
            if (cur->color == BLACK)
            fixRemoving(cur);
        dad->RIGHT == cur ? dad->RIGHT = nil : dad->LEFT = nil; 
    }
} else if (!(cur->RIGHT != nil && cur->LEFT != nil)) { // If there's only one children for current node
    Node* dad = cur->ancestor;
    Node* son;
    cur->RIGHT != nil ? son = cur->RIGHT : son = cur->LEFT; // Searching for son
    dad->RIGHT == cur ? dad->RIGHT = son : dad->LEFT = son; // Dad now points to son
    son->ancestor = dad;
    son->color = BLACK; // Changing son's color
    if (cur->color == BLACK) {
        fixRemoving(son);
    }
} else { // If left and right child exist
    Node* found = findMin(cur->RIGHT); // Finding the least element in right subtree
    cur->data = found->data; // Copying data
    removeNode(found); // Recurrently remove found node
}
return cur;

修复删除功能:

void RbTree::fixRemoving(Node*& node) {
Node* dad = node->ancestor;
Node* bro;
if (node == dad->LEFT) {
    bro = dad->RIGHT;
    if (bro->color == RED) {
        bro->color = BLACK;
        dad->color = RED;
        leftRotate(dad);
        bro = dad->RIGHT;
    }
    Node* leftNephew = bro->LEFT;
    Node* rightNephew = bro->RIGHT;
    if (leftNephew->color == BLACK && rightNephew->color == BLACK) { // 1) If both of bro's children are black
        bro->color = RED;
        dad->color = BLACK; 
        fixRemoving(dad);
        return;

    } else if (leftNephew->color == RED) { // 2) Else if only left is red - we need to make those steps and go to 3)
        leftNephew->color = BLACK;
        bro->color = RED;
        rightRotate(bro); 
    }
    rightNephew->color = BLACK; // 3) Now left nephew is black and right is red
    bro->color = dad->color;
    dad->color = BLACK;
    leftRotate(dad);
} else { // Same things there
    bro = dad->LEFT;
    if (bro->color == RED) {
        bro->color = BLACK;
        dad->color = RED;
        rightRotate(dad);
        bro = dad->LEFT;
    }
    Node* leftNephew = bro->LEFT;
    Node* rightNephew = bro->RIGHT;
    if (leftNephew->color == BLACK && rightNephew->color == BLACK) {
        bro->color = RED;
        dad->color = BLACK;
        fixRemoving(dad);
        return;
    } else if (leftNephew->color == RED) {
        leftNephew->color = BLACK;
        bro->color = RED;
        rightRotate(bro);
    }
    rightNephew->color = BLACK;
    bro->color = dad->color;
    dad->color = BLACK;
    rightRotate(dad);
}

我想我可能有以下问题:

  1. 使用正确的节点参数调用 fixRemoving 函数
  2. fixRemoving 方法的主体

我正在使用的算法:

  1. 找到一个我们想要删除的节点,就像我们在普通 BST 中所做的那样。
  2. 如果它没有 children - 只需删除它,如果它只有 1 children - 我们放置这个 child 而不是可移动节点,将其着色为黑色。
  3. 如果它同时具有 children - 我们在右子树中找到最少的元素并循环删除他。
  4. 如果节点的原始颜色是黑色 - 那么我们需要修复一棵树。

修复算法已在代码中描述 - 我希望不需要额外的信息。

求求你帮忙!

我基本同意你的算法。 但我不同意在只有 1-child 时尝试修复节点。 找到要删除的节点后 - 计算其 children 在 3 种情况下,删除节点很容易,成本也很低。 那些是

  1. 0-children 节点为红色 => 直接删除它
  2. 0-children且该节点为根节点=>删除即可
  3. 1-child,所有情况。实际情况是要删除的节点是黑色的,而 child 是红色的。 其他 3 种可能性都是无效的 Red-Black 树,因此不会发生。 解决方案是删除节点,替换为 child,并将 child 设为黑色。 不需要其他进一步的修正。

对于2-children,你需要找到继任者(右边child然后左边尽可能远)或前任(左边child然后右边最远)你可以走了)。 这个节点永远是0或者1child的情况,不可能是2。 我会测试 successor/predecessor 是否是“便宜删除”节点,如果不是,则选择 predecessor/successor。您的目标是获得便宜的删除。 然后,您需要使用 successor/predecessor 交换要删除的节点,但保留原始颜色。交换了节点(我的意思是所有指针链接)之后,您的问题就减少到删除一个具有 0 到 1 child 的节点,这可能很容易。

那还剩下什么?

也就是删除一个0-children节点,叶子节点,颜色为黑色,不是根节点。这是需要修正的情况,没有其他情况需要修正。