文章目录
前言
1、红黑树定义
○ 每个节点或是红色,或是黑色。
○ 根节点是黑色。
○ 每个叶子节点(最后的空节点,和以前的叶子节点概念不一样)是黑色的。
○ 红色节点的孩子是黑色的。 任意节点到叶子节点,经过的黑色节点数是一样的。
2、红黑树性能
完全随机的数据,二分搜索树就很好用了。极端情况会退化成链表。
红黑树:添加删除查询都是2logn。翻转更少,相对来说添加比AVL要快。
红黑树:树高2logn,牺牲了一定的平衡性;但是统计性能更优(增删改查所有操作),即平均性能。
只查询的话,AVL更好;红黑树更均衡。
3、红黑树特点
红黑树的各种东西太琐碎了,都是总结起来,梳理成的逻辑。看红黑树之前最好看一下2-3树。
红黑树的删除节点比添加节点还要复杂,更深入了解,再来填坑。
这里讨论的都是左倾红黑树。
一、重点解析
1、插入规则
1、1、插入节点旁没有红色节点:
-
插入的元素在黑节点的左侧,直接添加即可;
-
插入的元素在黑节点的右侧,将其左旋转,新根节点颜色继承,新孩子节点颜色变红色。
1、2、插入节点周围有红色节点:
-
插入的元素的父节点是黑色,父节点另一个孩子是红色。直接颜色翻转即可(对应于2-3树中,3-节点插入作为临时4-节点,再分裂成为三个2-节点。然后根节点变红,继续往上融合)
-
插入的节点是黑节点的红节点的孩子位置,需要右旋转后反转颜色。
-
插入的节点是红色节点的右边。只需要先左旋转,就变成了上面的情况。
以下就是三种复杂的插入时候的情况。
即每次插入完总是下面三种情况,只需要判断是否需要左旋转、再判断是否需要右旋转、再判断是否需要颜色反转即可。
对于1.1中,插入红节点为左倾,直接不用管;插入红节点要右倾,就左旋转。即可塞入下图第二个节点的模样中。
所有的案例的逻辑为:
右孩子是红,左孩子不是红 ->左旋转
左孩子是红,左孩子的左孩子是红 ->左旋转
右孩子是红,左孩子是红 ->颜色翻转
2、左旋转、右旋转
// node x
// / \ 左旋转 / \
// T1 x ---------> node T3
// / \ / \
// T2 T3 T1 T2
// node x
// / \ 右旋转 / \
// x T2 -------> y node
// / \ / \
// y T1 T1 T2
3、颜色翻转
4、插入节点
相对于BST,插入节点加入了:
- 对当前节点的高度计算;
- 对平衡因子的计算(左右子树的高度计算);
- 根据平衡性,分四种情况进行左旋右旋->以恢复平衡。
// 向以node为根的二叉搜索树中,插入节点(key, value)
// 返回插入新节点后的二叉搜索树的根
Node *add(Node *node, Key key, Value value) {
if (node == nullptr) {
size++;
return new Node(key, value);
}
if (key == node->key) {
node->value = value;
} else if (key < node->key) {
node->left = add(node->left, key, value);
} else {
node->right = add(node->right, key, value);
}
node->height = 1 + std::max(getHeight(node->left), getHeight(node->right));
int balanceFactor = getBalanceFactor(node);
if (balanceFactor > 1 && getBalanceFactor(node->left) >= 0) {
return rightRotate(node);
}
if (balanceFactor < -1 && getBalanceFactor(node->right) <= 0) {
return leftRotate(node);
}
if (balanceFactor > 1 && getBalanceFactor(node->left) < 0) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
if (balanceFactor < -1 && getBalanceFactor(node->right) > 0) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
5、析构函数
析构函数,需要用到后序遍历的思想。
即,删除当前节点,得先将其左右子树都删除掉,否则无法删除整个树。
下面代码,其实就是后序遍历的递归形式的析构函数。
// 析构函数, 释放二分搜索树的所有空间
~BST(){
destroy( root );
}
// 释放以node为根的二分搜索树的所有节点
// 采用后续遍历的递归算法
void destroy(Node* node){
if( node != NULL ){
destroy( node->left );
destroy( node->right );
delete node;
count --;
}
}
二、完整红黑树类
完整代码取自bobo老师及相关同学的github,文末有路径。
template<typename Key, typename Value>
class RBTree {
private:
static const bool RED = true;
static const bool BLACK = false;
struct Node {
Key key;
Value value;
Node *left;
Node *right;
bool color;
Node(Key key, Value value) {
this->key = key;
this->value = value;
this->left = this->right = nullptr;
color = RED;
}
Node(Node *node) {
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
this->color = node->color;
}
};
Node *root;
int size;
public:
RBTree() {
root = nullptr;
size = 0;
}
~RBTree() {
destroy(root);
}
int getSize() {
return size;
}
int isEmpty() {
return size == 0;
}
bool isRed(Node *node) {
if (node == nullptr) {
return BLACK;
}
return node->color;
}
void add(Key key, Value value) {
root = add(root, key, value);
root->color = BLACK;
}
bool contains(Key key) {
return getNode(root, key) != nullptr;
}
Value *get(Key key) {
Node *node = getNode(root, key);
return node == nullptr ? nullptr : &(node->value);
}
void set(Key key, Value newValue) {
Node *node = getNode(root, key);
if (node != nullptr) {
node->value = newValue;
}
}
private:
// 向以node为根的二叉搜索树中,插入节点(key, value)
// 返回插入新节点后的二叉搜索树的根
Node *add(Node *node, Key key, Value value) {
if (node == nullptr) {
size++;
return new Node(key, value);
}
if (key == node->key) {
node->value = value;
} else if (key < node->key) {
node->left = add(node->left, key, value);
} else {
node->right = add(node->right, key, value);
}
if (isRed(node->right) && !isRed(node->left)) {
node = leftRotate(node);
}
if (isRed(node->left) && isRed(node->left->left)) {
node = rightRotate(node);
}
if (isRed(node->left) && isRed(node->right)) {
flipColors(node);
}
return node;
}
// 在以node为根的二叉搜索树中查找key所对应的Node
Node *getNode(Node *node, Key key) {
if (node == nullptr) {
return nullptr;
}
if (key == node->key) {
return node;
} else if (key < node->key) {
return getNode(node->left, key);
} else {
return getNode(node->right, key);
}
}
void destroy(Node *node) {
if (node != nullptr) {
destroy(node->left);
destroy(node->right);
delete node;
size--;
}
}
Node *leftRotate(Node *node) {
Node *x = node->right;
node->right = x->left;
x->left = node;
x->color = node->color;
node->color = RED;
return x;
}
Node *rightRotate(Node *node) {
Node *x = node->left;
node->left = x->right;
x->right = node;
x->color = node->color;
node->color = RED;
return x;
}
void flipColors(Node *node) {
node->color = RED;
node->left->color = BLACK;
node->right->color = BLACK;
}
};