目录
红黑树的概念
![](https://i-blog.csdnimg.cn/blog_migrate/4888acb72a61bb9694f1e53b6aa2c0db.png)
1. 每个结点不是红色就是黑色2. 根节点是黑色的3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(不存在连续的红色节点)4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )
有了上述限制,最短路径就是只包含黑色节点的路径,而最长路径就是黑红交替,所以所有路径长度范围都在[最短路径,最短路径的2倍]这个区间之间, 保证了红黑树近似平衡
红黑树的节点结构定义
//颜色定义成枚举类型
enum Color
{
RED,
BLACK
};
//节点结构定义
template<class K, class V>
struct RBTreeNode
{
//三叉链
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
//键值对
pair<K, V> _kv;
//颜色变量
Color _col;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
红黑树的插入
插入新节点我们选择插入红色节点,理由如下:
1.插入黑色节点,一定破坏性质4,所有路径的黑色节点个数都要变化
2.插入红色节点,可能破坏性质3,插入红色节点的父亲是黑色节点,没有破坏性质3, 不需要调整;插入红色节点的父亲是红色节点,破坏了性质3, 需要进行调整
综合考虑,插入红色节点的代价相比插入黑色节点小很多,因此我们选择插入红色节点
插入情况的分类:
上面已经提到,插入红色节点的父亲是黑色节点,没有破坏性质3,因此无需调整,因此下面只讨论插入红色节点的父亲是红色节点的情况
处理方法总体分两类
1.插入节点的叔叔节点存在且为红色 --- 变色即可
字母含义: cur --- 插入节点, p --- cur的父亲节点, g --- cur的爷爷节点, u --- cur的叔叔节点
ps: g一定存在,因为p是红色节点, 不可能为根节点, 而g也一定是黑色节点,因为如果是红色节点,说明在cur插入之前红黑树就已经出问题了!
插入之后cur和p都是红色,因为不能出现连续红色节点,我们选择把p变黑,而p变黑之后,g-p-cur的这条路径多了一个黑色节点,因此我们又把g变红,g变红之后,g-u的这条路径又少了一个黑色节点,由于叔叔节点存在且为红色,因此我们又把u变黑即可
ps:变色完之后,发现g变成了红色,而上图的树可能只是一颗子树,因此g变红之后,接着:
1.如果g是根,把g变黑,因为红黑树要求根节点是黑色
2.如果g不是根,g必然有父亲,继续判断g的父亲节点的颜色,如果g的父节点是黑色,停止处理(因为没有再违反红黑树规则了);如果g的父节点是红色,此时又出现了连续的红节点,需要继续处理, 把cur更新到g的位置,继续循环处理!
2.插入节点的叔叔节不存在 或者 存在且为黑色 --- 旋转 + 变色
这种情况下,单纯的变色已经解决不了问题了!需要 旋转+变色 来处理
具体采用哪种旋转方法,在前面的博客 实现AVL树-CSDN博客 已经讲解过了
注意:无论是哪种旋转+变色方式,发现最终当前子树的根节点都变成了黑色,此时无论cur的父节点是黑色还是红色都不违反红黑树的规则,因此旋转+变色完成之后,直接break跳出循环即可!
右单旋 + 变色 (p是g的左,cur是p的左)
左右双旋 + 变色 (p是g的左,cur是p的右)
左单旋 + 变色 (p是g的右,cur是p的右)
右左双旋 + 变色 (p是g的右,cur是p的左)
红黑树旋转代码(除了不需要调平衡因子,其他代码与AVL树一样):
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (subRL)
subRL->_parent = parent;
if (parentParent == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
//左右双旋
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
//右左双旋
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
红黑树插入代码:
bool Insert(const pair<K, V>& kv)
{
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;
}
while (parent && parent->_col == RED) //父亲不存在或者父亲为黑色就不需要继续调整了!
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent) //父亲是爷爷的左孩子
{
Node* uncle = grandfather->_right;
//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//调整完之后,更新cur, 判断是否还需要调整
cur = grandfather;
parent = cur->_parent;
}
//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色
else
{
if (cur == parent->_left) //单纯左边高
{
RotateR(grandfather); //右单旋
//变色
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur是parent的右边
{
RotateLR(grandfather); //左右双旋
//变色
cur->_col = BLACK;
grandfather->_col = RED;
}
break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整
}
}
else //父亲是爷爷的右孩子
{
Node* uncle = grandfather->_left;
//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//调整完之后,更新cur, 判断是否还需要调整
cur = grandfather;
parent = cur->_parent;
}
//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色
else
{
if (cur == parent->_left) //cur是parent的左边,进行右单旋
{
RotateRL(grandfather); //右左单旋
//变色
cur->_col = BLACK;
grandfather->_col = RED;
}
else //cur是parent的右边
{
RotateL(grandfather); //左单旋
//变色
grandfather->_col = RED;
parent->_col = BLACK;
}
break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整
}
}
}
_root->_col = BLACK;
return true;
}
红黑树的验证
判断是否是红黑树
bool IsRBTree()
{
if (_root == nullptr) return true;
if (_root->_col == RED)
{
cout << "根节点为红色" << endl;
return false;
}
//以最左路径的黑节点的个数作为参考值
Node* cur = _root;
int BlackCount = 0;
while (cur)
{
if (cur->_col == BLACK)
BlackCount++;
cur = cur->_left;
}
//调用子函数判断红黑树
int count = 0;
return _IsRBTree(_root, count, BlackCount);
}
private:
bool _IsRBTree(Node* root, int count, int BlackCount)
{
if (root == nullptr)
{
if (count != BlackCount)
{
cout << "黑色节点的个数不相等" << endl;
return false;
}
return true;
}
//判断节点的孩子节点颜色不好判断, 转化成判断父亲节点颜色
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续的红节点" << endl;
return false;
}
if (root->_col == BLACK)
count++;
//递归子问题
return _IsRBTree(root->_left, count, BlackCount)
&& _IsRBTree(root->_right, count, BlackCount);
}
验证红黑树
#include "RBTree.h"
#include <vector>
void test1()
{
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
RBTree<int, int> t;
for (auto e : a)
{
t.Insert(make_pair(e, e));
}
t.IsRBTree();
cout << t.IsRBTree() << endl;
}
void test2()
{
const int N = 30;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand());
cout << v.back() << endl;
}
RBTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
cout << "Insert:" << e << "->" << t.IsRBTree() << endl;
}
cout << t.IsRBTree() << endl;
}
int main()
{
//test1();
test2();
return 0;
}
实现红黑树完整代码
#pragma once
#include <iostream>
using namespace std;
//颜色定义成枚举类型
enum Color
{
RED,
BLACK
};
//节点结构定义
template<class K, class V>
struct RBTreeNode
{
//三叉链
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
//键值对
pair<K, V> _kv;
//颜色变量
Color _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)
{
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;
}
while (parent && parent->_col == RED) //父亲不存在或者父亲为黑色就不需要继续调整了!
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent) //父亲是爷爷的左孩子
{
Node* uncle = grandfather->_right;
//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//调整完之后,更新cur, 判断是否还需要调整
cur = grandfather;
parent = cur->_parent;
}
//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色
else
{
if (cur == parent->_left) //单纯左边高
{
RotateR(grandfather); //右单旋
//变色
parent->_col = BLACK;
grandfather->_col = RED;
}
else //cur是parent的右边
{
RotateLR(grandfather); //左右双旋
//变色
cur->_col = BLACK;
grandfather->_col = RED;
}
break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整
}
}
else //父亲是爷爷的右孩子
{
Node* uncle = grandfather->_left;
//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//调整完之后,更新cur, 判断是否还需要调整
cur = grandfather;
parent = cur->_parent;
}
//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色
else
{
if (cur == parent->_left) //cur是parent的左边,进行右单旋
{
RotateRL(grandfather); //右左单旋
//变色
cur->_col = BLACK;
grandfather->_col = RED;
}
else //cur是parent的右边
{
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->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (subRL)
subRL->_parent = parent;
if (parentParent == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
//右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
//左右双旋
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
//右左双旋
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
//判断是否是红黑树
bool IsRBTree()
{
if (_root == nullptr) return true;
if (_root->_col == RED)
{
cout << "根节点为红色" << endl;
return false;
}
//以最左路径的黑节点的个数作为参考值
Node* cur = _root;
int BlackCount = 0;
while (cur)
{
if (cur->_col == BLACK)
BlackCount++;
cur = cur->_left;
}
//调用子函数判断红黑树
int count = 0;
return _IsRBTree(_root, count, BlackCount);
}
private:
bool _IsRBTree(Node* root, int count, int BlackCount)
{
if (root == nullptr)
{
if (count != BlackCount)
{
cout << "黑色节点的个数不相等" << endl;
return false;
}
return true;
}
//判断节点的孩子节点颜色不好判断, 转化成判断父亲节点颜色
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续的红节点" << endl;
return false;
}
if (root->_col == BLACK)
count++;
//递归子问题
return _IsRBTree(root->_left, count, BlackCount)
&& _IsRBTree(root->_right, count, BlackCount);
}
private:
Node* _root = nullptr;
};