先推荐一个在线红黑树的网站https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
在理解删除的时候帮了大忙
目录
3.1.2兄弟节点是黑色,兄弟节点的孩子均为黑色(或NIL)
3.2.2兄弟节点是黑色,兄弟节点的孩子均为黑色(或NIL)
节点定义
#pragma once
enum COLOR { RED = 0, BLACK = 1 };
template<typename KEY, typename VALUE>
class RBTreeNode {
public:
COLOR color;
KEY key;
VALUE value;
RBTreeNode<KEY, VALUE>* parent;
RBTreeNode<KEY, VALUE>* left;
RBTreeNode<KEY, VALUE>* right;
RBTreeNode() :color(BLACK), parent(nullptr), left(nullptr), right(nullptr) {}
RBTreeNode(const KEY& key, const VALUE& value, const COLOR& color = RED) :
color(color), key(key), value(value), parent(nullptr), left(nullptr), right(nullptr) {}
};
性质
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到每个null结点的路径都包含数量相同的黑结点。
红黑树的叶子节点并不是我们平常熟悉的度为0的节点(尽管我的代码里写的是null)
红黑树保证了从根到叶子的最长的简单路径不多于最短的可能路径的两倍长
(假设最短的全是黑的,那最长的就是在黑的中间插入红色,n个可以最多可以插n-1个红色)
平衡
平衡手段:旋转和变色
旋转就是AVL里的旋转
变色就是红色变为黑色,黑色变为红色
插入
我们插入的节点初始化为红色,插入方法和二叉排序树的时候一样
但是插入玩有可能破坏平衡,所以要调整
/**
* 插入/更新,返回节点
* @param key 键
* @param value 值
* @return 节点
*/
template<typename KEY, typename VALUE>
RBTreeNode<KEY, VALUE>* RBTree<KEY, VALUE>::insert_return(const KEY& key, const VALUE& value) {
RBTreeNode<KEY, VALUE>* insert_parent = nullptr;
RBTreeNode<KEY, VALUE>* temp = this->root;
while (nullptr != temp) {
//大于当前节点,往右找
if (temp->key < key) {
insert_parent = temp;
temp = temp->right;
}
//小于当前节点,往左
else if (temp->key > key) {
insert_parent = temp;
temp = temp->left;
}
//更新
else {
temp->value = value;
return temp;
}
}
auto* insert_node = new RBTreeNode<KEY, VALUE>(key, value, RED);
//空树
if (nullptr == insert_parent) {
this->root = insert_node;
}
else {
//比双亲大
if (insert_parent->key < key) {
insert_parent->right = insert_node;
}
//比双亲小
else {
insert_parent->left = insert_node;
}
insert_node->parent = insert_parent;
}
//修复插入的节点
insert_fix(insert_node);
return insert_node;
}
情景1:空树,插入完调整为黑色
情景2:已经key存在
更新value(类似"faQ"->1更新为"faQ"->2)
情景3:插入的双亲是黑色的
不用管
情景4:插入的双亲是红色
4.1双亲节点是祖父节点的左孩子
4.1.1叔叔节点存在且为红
由于叔叔是红的,祖父必然是黑的(不然违反性质4)
现在由于插入的是红色的,双亲是红色的,违反了性质4,所以,我们把双亲和叔叔调成黑色,祖父调为红色
然后再调整祖父
(插入节点的不要求是父亲节点的左孩子还是右孩子)
//插入节点双亲是左孩子
if (fix_node->parent == fix_node->parent->parent->left) {
//叔叔
uncle = fix_node->parent->parent->right;
//双亲和叔叔都是红的
if (nullptr != uncle && RED == uncle->color) {
//双亲和叔叔改为黑色,祖父改为红色
fix_node->parent->color = BLACK;
uncle->color = BLACK;
uncle->parent->color = RED;
//调整祖父
fix_node = uncle->parent;
}
4.1.2叔叔为黑色(或者NIL),插入节点是双亲的右孩子
对双亲做左旋(RR旋转),转为4.1.3
else {
//插入节点是右孩子
if (fix_node == fix_node->parent->right) {
fix_node = fix_node->parent;
//左旋
left_rotate(fix_node);
//调整为左孩子
}
//插入节点是左孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//右旋
right_rotate(fix_node->parent->parent);
}
4.1.3叔叔为黑色(或者NIL),插入节点是双亲的左孩子
双亲设置为黑色,祖父设置成红色,对祖父做右旋(LL旋转),调整完之后,插入节点的父亲是黑色的(也就是转换为了情景3),结束调整
else {
//插入节点是右孩子
if (fix_node == fix_node->parent->right) {
fix_node = fix_node->parent;
//左旋
left_rotate(fix_node);
//调整为左孩子
}
//插入节点是左孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//右旋
right_rotate(fix_node->parent->parent);
}
4.2双亲节点是祖父节点的右孩子
其实和4.1是对称的
4.2.1叔叔节点存在且为红
//双亲节点的右孩子
else {
//叔叔
uncle = fix_node->parent->parent->left;
//双亲和叔叔都是红的
if (nullptr != uncle && RED == uncle->color) {
//双亲和叔叔改为黑色,祖父改为红色
fix_node->parent->color = BLACK;
uncle->color = BLACK;
uncle->parent->color = RED;
//调整祖父
fix_node = uncle->parent;
}
4.2.2叔叔为黑色(或者NIL),插入节点是双亲的左孩子
转为4.2.3
else {
//插入节点是左孩子
if (fix_node == fix_node->parent->left) {
fix_node = fix_node->parent;
//右旋
right_rotate(fix_node);
//调整为右孩子
}
//插入节点是右孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//左旋
left_rotate(fix_node->parent->parent);
}
4.2.3叔叔为黑色(或者NIL),插入节点是双亲的右孩子
else {
//插入节点是左孩子
if (fix_node == fix_node->parent->left) {
fix_node = fix_node->parent;
//右旋
right_rotate(fix_node);
//调整为右孩子
}
//插入节点是右孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//左旋
left_rotate(fix_node->parent->parent);
}
-----------------------------------------------------
完整的调整
RBTreeNode<KEY, VALUE>* uncle = nullptr;
//双亲节点是黑色的时候不用调整
//调整红色节点
while (nullptr != fix_node->parent && RED == fix_node->parent->color) {
//插入节点双亲是左孩子
if (fix_node->parent == fix_node->parent->parent->left) {
//叔叔
uncle = fix_node->parent->parent->right;
//双亲和叔叔都是红的
if (nullptr != uncle && RED == uncle->color) {
//双亲和叔叔改为黑色,祖父改为红色
fix_node->parent->color = BLACK;
uncle->color = BLACK;
uncle->parent->color = RED;
//调整祖父
fix_node = uncle->parent;
}
else {
//插入节点是右孩子
if (fix_node == fix_node->parent->right) {
fix_node = fix_node->parent;
//左旋
left_rotate(fix_node);
//调整为左孩子
}
//插入节点是左孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//右旋
right_rotate(fix_node->parent->parent);
}
}
//双亲节点的右孩子
else {
//叔叔
uncle = fix_node->parent->parent->left;
//双亲和叔叔都是红的
if (nullptr != uncle && RED == uncle->color) {
//双亲和叔叔改为黑色,祖父改为红色
fix_node->parent->color = BLACK;
uncle->color = BLACK;
uncle->parent->color = RED;
//调整祖父
fix_node = uncle->parent;
}
else {
//插入节点是左孩子
if (fix_node == fix_node->parent->left) {
fix_node = fix_node->parent;
//左旋
right_rotate(fix_node);
//调整为右孩子
}
//插入节点是右孩子
fix_node->parent->color = BLACK;
fix_node->parent->parent->color = RED;
//左旋
left_rotate(fix_node->parent->parent);
}
}
}
this->root->color = BLACK;
-------------------------------------------------------
删除
1.左子树非空,右子树是空,用左子树替代
2.左子树空,右子树是非空,用右子树代替
3.左子树空,右子树是空,直接删除
4.左子树非空且右子树非空:找中序遍历的前驱节点或者后继来代替删除节点,然后删除代替节点(看看对应1,2,3哪种删除情况)
之后调整删除节点的孩子(如果空,则创建一个临时的null节点,辅助删除)
//这时候用null代替NIL节点就有点麻烦了,所以我创建了一个临时的null节点,来辅助删除
RBTreeNode<KEY, VALUE>* delete_point = find(key);
//不存在删除节点
if (nullptr == delete_point) {
return;
}
//左子树右子树非空,找找中序遍历前驱,然后删除前驱(也可以找后继)
if (nullptr != delete_point->left && nullptr != delete_point->right) {
//前驱
RBTreeNode<KEY, VALUE>* predecessor = in_order_predecessor(delete_point);
//替换
delete_point->key = predecessor->key;
delete_point->value = predecessor->value;
//删除节点改为前驱
delete_point = predecessor;
}
//是否创建了一个null辅助删除
bool fake_null_flag = false;
RBTreeNode<KEY, VALUE>* delete_point_child;
//找孩子
if (nullptr != delete_point->right) {
delete_point_child = delete_point->right;
}
else if (nullptr != delete_point->left) {
delete_point_child = delete_point->left;
}
else {
//黑色
delete_point_child = new RBTreeNode<KEY, VALUE>;
fake_null_flag = true;
}
//更新孩子的双亲
delete_point_child->parent = delete_point->parent;
//删的是根
if (nullptr == delete_point->parent) {
this->root = delete_point_child;
}
//删的是右子树
else if (delete_point == delete_point->parent->right) {
delete_point->parent->right = delete_point_child;
}
//删的是左子树
else {
delete_point->parent->left = delete_point_child;
}
//删除红色不用调整,空树也不用调整
//调整黑色的节点
if (delete_point->color == BLACK) {
delete_fix(delete_point_child);
}
//处理虚假的null
if (fake_null_flag) {
if (nullptr != delete_point_child->parent) {
if (delete_point_child->parent->left == delete_point_child) {
delete_point_child->parent->left = nullptr;
}
else if (delete_point_child->parent->right == delete_point_child) {
delete_point_child->parent->right = nullptr;
}
}
else {
this->root = nullptr;
}
delete delete_point_child;
delete_point_child = nullptr;
}
delete delete_point;
delete_point = nullptr;
情景1 :节点是红色的
染成黑色,结束
情景2:节点是黑色而且是根节点
直接结束
情景3:删除节点是黑色且不是根
3.1调整节点是双亲结点的左子树
3.1.1兄弟节点存在,且为红色
将双亲节点改为红色,兄弟节点改为黑色,对双亲左旋,转换为情景3.1.2/3.1.3/3.1.4的一种
//删除节点是左孩子
if (fix_node == fix_node->parent->left) {
//兄弟节点
RBTreeNode<KEY, VALUE>* brother = fix_node->parent->right;
//兄弟是红的(父亲节点和兄弟的孩子均为黑色)
if (nullptr != brother && RED == brother->color) {
brother->color = BLACK;
brother->parent->color = RED;
left_rotate(fix_node->parent);
}
3.1.2兄弟节点是黑色,兄弟节点的孩子均为黑色(或NIL)
双亲是什么颜色无所谓
此时违反了性质5,所以让右子树少一个黑色节点
兄弟节点调为红色,然后调整节点改为双亲结点
//兄弟是黑的
else {
//兄弟的孩子均为黑的(空的也是黑)
if ((brother->left == nullptr || BLACK == brother->left->color) &&
(brother->right == nullptr || BLACK == brother->right->color)) {
//兄弟改成红色
brother->color = RED;
fix_node = fix_node->parent;
}
3.1.3兄弟节点是黑色,兄弟节点的右孩子是黑色
兄弟节点的左子树什么颜色无所谓
兄弟借点改为红色
兄弟左孩子改为黑色
转换为3.1.4
else {
//兄弟右孩子是黑的(左孩子任意)
if (brother->right == nullptr || BLACK == brother->right->color) {
brother->color = RED;
if (nullptr != brother->left) {
brother->left->color = BLACK;
}
right_rotate(brother);
brother = fix_node->parent->right;
}
//兄弟有黑子是红色的
brother->color = fix_node->parent->color;
brother->parent->color = BLACK;
if (nullptr != brother->right) {
brother->right->color = BLACK;
}
left_rotate(fix_node->parent);
fix_node = this->root;
}
3.1.4兄弟节点是黑色,兄弟右孩子为红色
兄弟的左节点无所谓
兄弟节点调整为双亲结点的颜色,双亲和兄弟的右孩子调整为黑色,结束
else {
//兄弟右孩子是黑的(左孩子任意)
if (brother->right == nullptr || BLACK == brother->right->color) {
brother->color = RED;
if (nullptr != brother->left) {
brother->left->color = BLACK;
}
right_rotate(brother);
brother = fix_node->parent->right;
}
//兄弟有黑子是红色的
brother->color = fix_node->parent->color;
brother->parent->color = BLACK;
if (nullptr != brother->right) {
brother->right->color = BLACK;
}
left_rotate(fix_node->parent);
fix_node = this->root;
}
3.2调整节点是双亲结点的右子树
3.2.1兄弟节点存在,且为红色
转换为情景3.2.2/3.2.3/3.2.4的一种
//删除节点是右孩子
else {
//兄弟节点
RBTreeNode<KEY, VALUE>* brother = fix_node->parent->left;
//兄弟是红的(父亲节点和兄弟的孩子均为黑色)
if (nullptr != brother && RED == brother->color) {
brother->color = BLACK;
brother->parent->color = RED;
right_rotate(fix_node->parent);
}
3.2.2兄弟节点是黑色,兄弟节点的孩子均为黑色(或NIL)
双亲是什么颜色无所谓
//兄弟是黑的
else {
//兄弟的孩子均为黑的(空的也是黑)
if ((brother->left == nullptr || BLACK == brother->left->color) &&
(brother->right == nullptr || BLACK == brother->right->color)) {
//兄弟改成红色
brother->color = RED;
fix_node = fix_node->parent;
}
3.2.3兄弟节点是黑色,兄弟节点的右孩子是黑色
兄弟节点的左子树什么颜色无所谓
转换为3.2.4
else {
//兄弟左孩子是黑的(右孩子任意)
if (brother->left == nullptr || BLACK == brother->left->color) {
brother->color = RED;
if (nullptr != brother->right) {
brother->right->color = BLACK;
}
left_rotate(brother);
}
//兄弟有黑子是红色的
brother->color = fix_node->parent->color;
brother->parent->color = BLACK;
if (nullptr != brother->left) {
brother->left->color = BLACK;
}
right_rotate(fix_node->parent);
fix_node = this->root;
}
3.2.4兄弟节点是黑色,兄弟右孩子为红色
兄弟的左节点无所谓
结束
else {
//兄弟左孩子是黑的(右孩子任意)
if (brother->left == nullptr || BLACK == brother->left->color) {
brother->color = RED;
if (nullptr != brother->right) {
brother->right->color = BLACK;
}
left_rotate(brother);
}
//兄弟有黑子是红色的
brother->color = fix_node->parent->color;
brother->parent->color = BLACK;
if (nullptr != brother->left) {
brother->left->color = BLACK;
}
right_rotate(fix_node->parent);
fix_node = this->root;
}
参考资料:
https://www.jianshu.com/p/e136ec79235c
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/03.01.md
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html