删除一个节点要保证根节点是黑色,每个路径黑色节点的数量要和删除之前保持一致
根据有孩子分为几种情况:
有两个孩子、有一个孩子(左孩子或有孩子)、没有孩子。
那么将有一个孩子的转换成有一个孩子或者没有孩子情况
找到前驱节点,前驱节点的覆盖到要删除的节点上,然后删除前驱节点,这样就转换成后两种情况.
根据删除节点的颜色分为几种情况
- 删除的是红色节点,删除之后不违反红黑树规则,不用做任何调整
- 删除的是黑色节点,分为两种情况:
① 如果补上来的孩子是红色节点,则直接把这个孩子涂成黑色,调整完成 ② 如果不上来的孩子是黑色节点,没有办法在这个路径补出一个黑色节点,需要从兄弟那里借一个黑色节点,这又分为4种情况:
下列都以删除节点B为例
情况1:
节点B的兄弟节点是黑色,兄弟节点的右子节点有一个红色(左、右子节点都为红色也归结为情况1)
情况2:
节点B的兄弟节点是黑色,兄弟节点只有左子节点是红色
情况3:
节点B的兄弟节点是黑色,但是兄弟节点没有红色的子节点,那么就让两边路径各自少一个黑色节点,然后父节点向上回溯,碰到红色节点就涂成黑色(调整完成),如果碰到了黑色节点,那么就按照其他三种情况处理。
情况4:
节点B的兄弟节点是红色
删除代码
template <typename T>
class RBTree
{
enum Color { BLACK = 1, RED };
struct Node;
public:
RBTree() {}
~RBTree() { /* 三种遍历方式进行析构 */ }
// 左旋
void leftRotate(Node* node)
{
if (node == nullptr) return;
Node* child = node->right;
setParent(child, parent(node));
if (parent(node) == nullptr)
m_root = child;
else
{
if (parent(node)->left == node)
parent(node)->left = child;
else
parent(node)->right = child;
}
node->right = child->left;
if (node->right) setParent(node->right, node);
child->left = node;
setParent(node, child);
}
// 右旋
void rightRotate(Node* node)
{
if (node == nullptr) return;
Node* child = node->left;
setParent(child, parent(node));
if (parent(node) == nullptr)
m_root = child; // 当前节点是根节点
else
{
if (parent(node)->left == node)
parent(node)->left = child;
else
parent(node)->right = child;
}
node->left = child->right;
if (node->left)
setParent(node->left, node);
child->right = node;
setParent(node, child);
}
// 删除
void remove(const T& data)
{
if (m_root == nullptr) return;
Node* node = m_root;
while (node)
{
if (node->data > data)
node = node->left; // 去左子树查找
else if (node->data < data)
node = node->right; // 去右子树查找
else break; // 找到
}
if (!node) return; // 没有找到则退出
// 有两个子节点的情况,转换为只有一个子节点或没子节点的情况
if (node->left && node->right)
{
// 找到前驱节点
Node* prev = node->left;
while (prev->right)
prev = prev->right;
node->data = prev->data; // 前驱节点的值覆盖当前节点, 然后删除前驱节点
node = prev;
}
Node* child = node->left;
if (child == nullptr)
child = node->right;
if (child != nullptr) // 只有一个子节点的情况
{
setParent(child, parent(node)); // 设置子节点的父节点
if (parent(node) == nullptr)
m_root = child; // 子节点是新的根节点
else
{
// 将父节点和子节点关联起来
if (parent(node)->left == node)
parent(node)->left = child;
else
parent(node)->right = child;
}
// 删除当前节点
Color c = color(node); // 记录删除节点的颜色
delete node;
if (c == BLACK) // 删除的是黑色节点需要调整
fixAfterRemove(child);
}
else // 两个子节点都为空
{
if (parent(node) == nullptr)
{
// 当前删除的节点就是根节点,而且没有子节点作为新的根节点
delete node;
m_root = nullptr;
return;
}
else
{
if (color(node) == BLACK) // 删除的是黑色节点需要调整
fixAfterRemove(node);
if (parent(node)->left == node)
parent(node)->left = nullptr;
else
parent(node)->right = nullptr;
delete node;
return;
}
}
}
void fixAfterRemove(Node* node) // 删除节点调整
{
while (m_root != node && color(node) == BLACK)
{
if (parent(node)->left == node)
{
// 删除的黑色节点在左子树
Node* brother = parent(node)->right;
if (color(brother) == RED) // 情况4
{
setColor(brother, BLACK);
setColor(parent(node), RED);
leftRotate(parent(node));
brother = parent(node)->right;
}
// 两个节点都为黑色
if (color(brother->left) == BLACK
&& color(brother->right) == BLACK) // 情况3
{
setColor(brother, RED);
node = parent(node);
}
else
{
// 进入这里表示有一个子节点肯定为红色,要不全是红色
if (color(brother->right) != RED) // 情况2
{
setColor(brother, RED);
setColor(brother->left, BLACK);
rightRotate(brother);
brother = parent(node)->right;
}
// 情况1
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(brother->right, BLACK);
leftRotate(parent(node));
break;
}
}
else
{
// 删除的黑色节点在右子树
Node* brother = parent(node)->left;
if (color(brother) == RED) // 情况4
{
setColor(brother, BLACK);
setColor(parent(node), RED);
rightRotate(parent(node));
brother = parent(node)->left;
}
// 两个节点都为黑色
if (color(brother->left) == BLACK
&& color(brother->right) == BLACK) // 情况3
{
setColor(brother, RED);
node = parent(node);
}
else
{
// 进入这里表示有一个子节点肯定为红色,要不全是红色
if (color(brother->left) != RED) // 情况2
{
setColor(brother, RED);
setColor(brother->right, BLACK);
leftRotate(brother);
brother = parent(node)->left;
}
// 情况1
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(brother->left , BLACK);
rightRotate(parent(node));
break;
}
}
}
// 当前节点是红色直接设置成黑色
setColor(node, BLACK);
}
};
测试代码
#include <iostream>
using namespace std;
int main()
{
RBTree<int> tree;
for (int i = 1; i <= 10; i++)
tree.insert(i);
tree.remove(9);
tree.remove(10);
tree.remove(5);
tree.remove(4);
return 0;
}