前言
之前我们学习了AVL树,AVL树其实是具有特殊性质的二叉搜索树,而红黑树这种数据结构也是有着特殊性质的二叉搜索树,甚至红黑树在某些层面上将是优于二叉搜索树的,这篇博客就带大家一起梳理一下红黑树这种复杂数据结构的原理以及实现。
一:红黑树
红黑树是一个具有着特殊性质的二叉搜索树,也叫近似平衡二叉搜索树,并且在每个结点上增加了存储该结点颜色的存储位,可以是红色或者黑色。 通过对任何一条从根到叶子结点的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍。
红黑树图解:
红黑树性质:
- 每个结点不是红色就是黑色
- 根结点是黑色的
- 如果一个结点是红色的,则它的两个孩子结点是黑色的(树中不存在连续的红色结点)
- 对于每个结点,从该结点到其所有后代叶子结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处叶子结点指的是空结点)
思考:为什么满足上述性质,红黑树就能保证其最长路径结点个数不会超过最短路径结点个数的两倍?
最短路径:全是黑色结点
最长路径:红黑交替
所以最长路径的结点个数不会超过最短路径结点个数的两倍
理论上而言红黑树的效率比AVL树的效率略差,但是目前CPU的运算速度非常的快,红黑树与AVL树的搜索效率之间没有明显差异。
目前而言,红黑树的运用更为广泛。因为红黑树是近似平衡二叉树,旋转的次数大大小于AVL树的旋转次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
1.1 红黑树的实现
代码实现:
- 按二叉搜索树的规则进行插入
- 插入的结点默认红色
- 红黑树调整主要取决于uncle结点
#pragma once
enum Colour{
BLACK,
RED,
};
template<class K,class V>
struct RBTreeNode{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
template<class K,class V>
class RBTree{
typedef RBTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv){
// 1. 按二叉搜索树的规则进行插入
if (_root == nullptr){
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur){
if (cur->_kv.first < kv.first){
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first){
parent = cur;
cur = cur->_left;
}
else{
return false;
}
}
// 找到插入的位置
cur = new Node(kv);
if (parent->_kv.first < kv.first){
parent->_right = cur;
cur->_parent = parent;
}
else{
parent->_left = cur;
cur->_parent = parent;
}
// 红黑树性质控制
// 插入结点默认红色(若默认黑色,则每条路径都会影响,而红色最多影响一条路径)
cur->_col = RED;
while (parent && parent->_col == RED){
// 红黑树调整重点在叔叔
Node* grandfather = parent->_parent;
if (grandfather->_left == parent){
// 叔叔在右
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED){
// 1. cur为红 parent为红 grandfather为黑 u存在且为红(变色处理)
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 向上处理
cur = grandfather;
parent = cur->_parent;
}
else{
// cur为红 parent为红 grandfather为黑 u不存在或u为黑(变色+折线双旋处理)
if (cur == parent->_right){
// 先左单旋
RotateL(parent);
// 交换转为第二种情况
swap(parent, cur);
}
// cur为红 parent为红 grandfather为黑 u不存在或u为黑(变色+直线单旋处理)
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
break;
}
}
else{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED){
// 1. cur为红 parent为红 grandfather为黑 u存在且为红(变色处理)
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 向上处理
cur = grandfather;
parent = cur->_parent;
}
else{
if (cur == parent->_left){
// 双旋转单旋
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
break;
}
}
}
_root->_col = BLACK;
return true;
}
// 左单旋
void RotateL(Node* parent){
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
parent->_right = SubRL; // subR的左 赋值给 parent的右
if (SubRL != nullptr){
SubRL->_parent = parent;
}
SubR->_left = parent; // parent 赋值给 subR的左
Node* ppNode = parent->_parent;
parent->_parent = SubR;
// 1.原来parent是树的根,现在变为SubR
if (parent == _root){
_root = SubR;
SubR->_parent = nullptr;
}
// 2.原来parent不是树的根
else{
if (ppNode->_left == parent){
ppNode->_left = SubR;
}
else{
ppNode->_right = SubR;
}
SubR->_parent = ppNode;
}
}
// 右单旋
void RotateR(Node* parent){
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
parent->_left = SubLR;
if (SubLR != nullptr){
SubLR->_parent = parent;
}
SubL->_right = parent;
Node* ppNode = parent->_parent;
parent->_parent = SubL;
if (parent == _root){
_root = SubL;
SubL->_parent = nullptr;
}
else{
if (ppNode->_left == parent){
ppNode->_left = SubL;
}
else{
ppNode->_right = SubL;
}
SubL->_parent = ppNode;
}
}
private:
Node* _root = nullptr;
};
1.2 红黑树的应用
- C++ STL库:map/set、mutil_map/mutil_set
- Java 库
- linux内核