问题描述
我正在尝试制作 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);
}
我想我可能有以下问题:
我正在使用的算法:
- 找到一个我们想要删除的节点,就像我们在普通 BST 中所做的那样。
- 如果它没有子节点 - 只需删除它,如果它只有 1 个子节点 - 我们放置这个子节点而不是可移动节点,将其着色为黑色。
- 如果它有两个孩子 - 我们在右子树中找到最少的元素并循环删除他。
- 如果节点的原始颜色是黑色 - 那么我们需要修复一棵树。
修复算法在代码中进行了描述 - 我希望不需要额外的信息。
请帮忙!
解决方法
我基本同意你的算法。 但是我不同意在只有 1 个孩子时尝试修复节点。 一旦找到要删除的节点 - 计算其子节点 在 3 种情况下,删除节点既容易又便宜。 那些是
- 0-children 并且节点是红色的 => 删除它
- 0-children 和节点是根节点 => 删除它
- 1-child,所有情况。实际情况是要删除的节点是黑色的,子节点是红色的。 其他 3 种可能性都是无效的红黑树,因此不会发生。 解决办法是删除节点,替换为子节点,让子节点变黑。 不需要其他进一步的修正。
对于 2-children,您需要找到后继(右孩子然后左尽可能远)或前任(左孩子然后右尽可能远)。 此节点将始终是 0 或 1 子案例,它不能是 2。 我会测试看看后继/前驱是否是一个“便宜的删除”节点,如果不是,选择前驱/后继。你的目标是获得便宜的删除。 然后,您需要将要删除的节点与后继/前驱交换,但保留原始颜色。交换节点(我的意思是所有指针链接)后,您的问题就简化为删除具有 0 到 1 个子节点的节点,这可能很容易。
还剩下什么?
即删除一个0-children节点,一个叶子节点,颜色为黑色,不是根节点。这是需要修正的情况,没有其他情况需要修正。