目录
- 红黑树不是一棵平衡树,节点的左右子树高度差长的不超过短的2倍。
红黑树与AVL树的比较:
操作 | AVL | 红黑树 |
平衡树 | 是 | 否 |
增删查时间复杂度 | O(logn) | O(logn) |
insert最多旋转的次数 | 2 | 2 |
remove最多旋转的次数 | O(logn) | 3 |
若场景中删除操作较多,则选红黑树,否则选AVL树,因为AVL树绝对平衡,查找效率高
红黑树的性质:
- 每一个节点都有颜色,不是黑色就是红色
- 所有叶子节点的孩子都是黑色 (即null是黑色)
- 根节点是黑色
- 不能出现连续的红色节点(即父亲若为红色,孩子就不能是红色)
- 从根节点到其他每个叶子节点的所有简单路径都包含相同的黑色节点
红黑树插入删除实现用到的接口代码:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
//红黑树
template<typename T>
class RBTree
{
public:
RBTree() :root_(nullptr) {}
//插入操作
void insert(const T &val)
{
if (root_ == nullptr)
{
root_ = new Node(val);
return;
}
Node *parent = nullptr;
Node *cur = root_;
while (cur != nullptr)
{
if (cur->data_ > val)
{
parent = cur;
cur = cur->left_;
}
else if (cur->data_ < val)
{
parent = cur;
cur = cur->right_;
}
else
{
return;
}
}
//设置当前节点的parent和颜色
Node *node = new Node(val,parent,nullptr,nullptr,RED);
if (parent->data_ > val)
{
parent->left_ = node;
}
else
{
parent->right_ = node;
}
//如果新插入的节点,父节点也是红色,不满足红黑树性质,进行插入调整操作
if (RED == color(parent))
{
fixAfterInsert(node);
}
}
//删除操作
void remove(const T &val)
{
if (root_ == nullptr)
{
return;
}
Node *cur = root_;
while (cur != nullptr)
{
if (cur->data_ > val)
{
cur = cur->left_;
}
else if (cur->data_ < val)
{
cur = cur->right_;
}
else
{
break;
}
}
if (cur == nullptr)
{
return;
}
//删除cur节点 情况3
if (cur->left_ != nullptr && cur->right_ != nullptr)
{
Node *pre = cur->left_;
while (pre->right_ != nullptr)
{
pre = pre->right_;
}
cur->data_ = pre->data_;
cur = pre;//cur指向前驱节点
}
//删除cur指向的节点 情况1和2
Node *child = cur->left_; //让child指向不为空的孩子
if (child == nullptr)
{
child = cur->right_;
}
if (child != nullptr)
{
child->parent_ = cur->parent_;
if (cur->parent_ == nullptr)
{
//开始root_ 指向cur_
root_ = child;
}
else
{
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = child;
}
else
{
cur->parent_->right_ = child;
}
}
Color c = color(cur);
delete cur;
if (c == BLACK)//删除的是黑色节点要进行调整操作
{
fixAfterRemove(child);
}
}
else //child == nullptr
{
if (cur->parent_ == nullptr)
{
delete cur;
root_ = nullptr;
return;
}
else
{
//删除的cur为叶子节点
if (color(cur) == BLACK)
{
fixAfterRemove(cur);
}
if (cur->parent_->left_ == cur)
{
cur->parent_->left_ = nullptr;
}
else
{
cur->parent_->right_ = nullptr;
}
delete cur;
}
}
}
private:
//节点颜色
enum Color
{
BLACK,
RED
};
//节点类型
struct Node
{
Node(T data = T(), Node *parent = nullptr,
Node *left = nullptr, Node *right = nullptr,
Color color = BLACK)
:data_(data)
, left_(left)
, right_(right)
, parent_(parent)
, color_(color)
{}
T data_;
Node *left_;
Node *right_;
Node *parent_;
Color color_;//节点的颜色
};
//返回节点的颜色
Color color(Node *node)
{
return node == nullptr ? BLACK : node->color_;
}
//设置节点颜色
void setColor(Node *node, Color color)
{
node->color_ = color;
}
//返回节点的左孩子
Node* left(Node *node)
{
return node->left_;
}
//返回节点的右孩子
Node* right(Node *node)
{
return node->right_;
}
//返回节点的父亲
Node* parent(Node *node)
{
return node->parent_;
}
//左旋转
void leftRotate(Node *node)
{
Node *child = node->right_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node 本身就是root节点
root_ = child;
}
else
{
if (node->parent_->left_ == node)
{
//node在父节点的左孩子
node->parent_->left_ = child;
}
else
{
//node在父节点的右孩子
node->parent_->right_ = child;
}
}
node->right_ = child->left_;
if (child->left_ != nullptr)
{
child->left_->parent_ = node;
}
child->left_ = node;
node -> parent_ = child;
}
//右旋转
void rightRotate(Node *node)
{
Node *child = node->left_;
child->parent_ = node->parent_;
if (node->parent_ == nullptr)
{
//node本身就是root_;
root_ = child;
}
else
{
if (node->parent_->right_ == node)
{
//node在父节点的右孩子
node->parent_->right_ = child;
}
else
{
//node在父节点的左孩子
node->parent_->left_ = child;
}
}
node->left_ = child->right_;
if (child->right_ != nullptr)
{
child->right_->parent_ = node;
}
child->right_ = node;
node->parent_ = child;
}
Node *root_;//指向红黑树的根节点
};
红黑树插入:
- 空树 ->插入 节点颜色:黑色
- 非空 插入节点 ->叶子节点 -> 节点颜色:红色(此时要检查父节点的颜色,若父节点是黑色,插入完成 。否则出现连续的红色节点,开始做红黑树的插入调整。)
调整右如下三种情况:
情况一如图:
A.color = RED
B.color = BLACK
D.color = BLACK
X=A 继续向上调整
情况二如图:
B.color = BLACK
A.color = RED
rightRotate(A)
情况三如图:
红黑树插入代码实现如下:
//红黑色的插入调整操作
void fixAfterInsert(Node *node)
{
//如果当前红色节点的父节点也是红色,继续调整
while (color(parent(node)) == RED)
{
if (left(parent(parent(node))) == parent(node))
{
//插入的节点在左子树当中
Node *uncle = right(parent(parent(node)));
if (RED == color(uncle)) //情况1
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node)); //继续调整
}
else
{
//先处理情况3
if (right(parent(node)) == node)
{
node = parent(node); //要将情况3的node指向处理成和情况2的指向一样
leftRotate(node);
}
//最后统一处理情况2
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
rightRotate(parent(parent(node)));
break;
}
}
else
{
//插入的节点在右子树当中
Node *uncle = left(parent(parent(node)));
if (RED == color(uncle)) //情况1
{
setColor(parent(node), BLACK);
setColor(uncle, BLACK);
setColor(parent(parent(node)), RED);
node = parent(parent(node)); //继续调整
}
else
{
//先处理情况3
if (left(parent(node)) == node)
{
node = parent(node); //要将情况3的node指向处理成和情况2的指向一样
rightRotate(node);
}
//最后统一处理情况2
setColor(parent(node), BLACK);
setColor(parent(parent(node)), RED);
leftRotate(parent(parent(node)));
break;
}
}
}
//此处强制root为黑色节点
setColor(root_, BLACK);
}
红黑树删除:
- 如果删除的是一个红色节点,就是一个BST树节点的删除,不用做任何删除调整操作
- 如果删除的是一个黑色节点,分两种情况
- 如果补上来的孩子是红色节点,直接把这个孩子涂成黑色,调整完成!
- 如果补上来的孩子是黑色节点,那么当前这个路径上没办法再补一个黑色节点出来了,只能从兄弟那里借调黑色节点了。 此情况有八种,分左右下图介绍右边四种情况:
情况一:兄弟在右边 左黑右红/左红右红
C.color = A.color
A.color = BLACK
leftRotate(A)
C.right.color = BLACK
情况二:左红右黑
D.color = BLACK
C.color = RED
rightRotate(C)
情况三:左黑右黑(此情况让兄弟变成红色,此时兄弟少一个黑色,然后找公共红色的点,改成黑色,找不到就回溯,将每条路径都减少一个黑色)
如上图调整过后的图的node指向A,node向上回溯发现指向的是红色节点,直接把红色节点涂成黑色,结束!若node指向的还是黑色节点,继续落在四种情况之内。
情况四:
A.color = RED
C.color = BLACK
leftRotate(A)
红黑树删除代码实现:
//红黑树的删除调整操作
void fixAfterRemove(Node *node)
{
while (node != root_ && color(node) == BLACK)
{
if (left(parent(node)) == node)
{
//删除的黑色节点在左子树
Node *brother = right(parent(node));
if (color(brother) == RED)//情况四
{
setColor(parent(node), RED);
setColor(brother, BLACK);
leftRotate(parent(node));
brother = right(parent(node));
}
if (color(left(brother)) == BLACK && color(right(brother)) == BLACK)
{
//情况三
setColor(brother, RED);
node = parent(node);
}
else
{
if (color(right(brother)) != RED)
{
//情况二
setColor(left(brother), BLACK);
setColor(brother, RED);
rightRotate(brother);
brother = right(parent(node));
}
//全部归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(right(brother), BLACK);
leftRotate(parent(node));
break;
}
}
else
{
//删除的黑色节点在右子树
Node *brother = left(parent(node));
if (color(brother) == RED)//情况四
{
setColor(parent(node), RED);
setColor(brother, BLACK);
rightRotate(parent(node));
brother = left(parent(node));
}
if (color(left(brother)) == BLACK && color(right(brother)) == BLACK)
{
//情况三
setColor(brother, RED);
node = parent(node);
}
else
{
if (color(left(brother)) != RED)
{
//情况二
setColor(right(brother), BLACK);
setColor(brother, RED);
leftRotate(brother);
brother = left(parent(node));
}
//全部归结到情况一
setColor(brother, color(parent(node)));
setColor(parent(node), BLACK);
setColor(left(brother), BLACK);
rightRotate(parent(node));
break;
}
}
}
//如果node节点颜色是红色 ,直接涂成黑色,调整结束
setColor(node, BLACK);
}