C++红黑树实现的问题

问题描述

我正在尝试制作 RB 树的 C++ 实现。但是当我尝试删除一个元素时 - 我发现树的一些叶子具有不同的黑色高度值。在调用删除函数之前一切正常 - 树插入后的插入和重新平衡工作正常。但是在一次调用 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. 如果它没有子节点 - 只需删除它,如果它只有 1 个子节点 - 我们放置这个子节点而不是可移动节点,将其着色为黑色。
  3. 如果它有两个孩子 - 我们在右子树中找到最少的元素并循环删除他。
  4. 如果节点的原始颜色是黑色 - 那么我们需要修复一棵树。

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

请帮忙!

解决方法

我基本同意你的算法。 但是我不同意在只有 1 个孩子时尝试修复节点。 一旦找到要删除的节点 - 计算其子节点 在 3 种情况下,删除节点既容易又便宜。 那些是

  1. 0-children 并且节点是红色的 => 删除它
  2. 0-children 和节点是根节点 => 删除它
  3. 1-child,所有情况。实际情况是要删除的节点是黑色的,子节点是红色的。 其他 3 种可能性都是无效的红黑树,因此不会发生。 解决办法是删除节点,替换为子节点,让子节点变黑。 不需要其他进一步的修正。

对于 2-children,您需要找到后继(右孩子然后左尽可能远)或前任(左孩子然后右尽可能远)。 此节点将始终是 0 或 1 子案例,它不能是 2。 我会测试看看后继/前驱是否是一个“便宜的删除”节点,如果不是,选择前驱/后继。你的目标是获得便宜的删除。 然后,您需要将要删除的节点与后继/前驱交换,但保留原始颜色。交换节点(我的意思是所有指针链接)后,您的问题就简化为删除具有 0 到 1 个子节点的节点,这可能很容易。

还剩下什么?

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