目录
红黑树概念
红黑树,是一种二叉搜索树,但在每个节点上增加一个存储位置表示节点的颜色,可以是red或black。通过对任意一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的,而AVL树是高度平衡的。C++STL中,很多部分(包括set,multiset,map,multimap)的底层都是红黑树。对于任意一颗红黑树,它可以在O(log2 N)时间内做查找,插入和删除,这里的N是树中元素的数目。
红黑树的性质
红黑树是每个节点都带有颜色属性的二叉查找树,颜色是红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树还增加了如下的额外要求:
- 每个节点不是红色就是黑色
- 根节点是黑色的
- 每个叶子节点都是黑色的(此处的叶子节点指的是空节点NIL)
- 如果一个节点是红色的,则它的两个孩子节点是黑色的
- 对于每个节点,从该节点到其后代叶子节点的简单路径上,均包含相同数目的黑色节点。
如图:
在红黑树的概念中提到红黑树是接近平衡的,因为红黑树确保没有一条路径会比其他路径长处两倍。为什么?
由于每个节点非红即黑(性质1),加上不存在两个连续的红节点(性质4),那么红黑树的最长路径一定是红黑交替的,而最短路径一定全部都是黑节点。而任意节点其所有后代叶节点的简单路径上,均包含相同数目的黑色节点,则说明最长路径和最短路径中的黑色节点数目一定相等。最后加上根节点为黑(性质2)和叶子节点为黑(性质3),那么一定可以得出一个结论:最长路径 <= 2 * 最短路径
红黑树的插入:
当我们在对红黑树进行插入操作时,对树做了修改,那么可能会违背红黑树的性质。为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些节点的颜色及指针结构,以达到对红黑树进行插入、删除节点等操作时,红黑树依旧能保持它特有的性质。
红黑树的插入分为以下情况,在了解之前,我们还要必须知道插入的节点是红色的,如果插入节点为黑色,总是会破环性质5,而插入红节点不一定会破坏性质5。因为在插入之前,该树已经是红黑树了,插入了黑节点肯定会影响该节点到根节点的这条路径。
我们将插入的节点标记为cur,父节点标记为p,祖父节点为g,叔叔节点为u。
①该树为空树,直接插入根节点的位置,再把节点颜色改为黑色即可。
②插入节点 cur的父节点p为黑色,不违反任何性质,无需做任何修改。
③cur为红,p为红(祖父节点一定存在,且为黑,下边同理)u也为红,这里不论p是g的左孩子,还是右孩子;不论cur是p的左孩子,还是右孩子。
为什么在这个情况下,祖父节点一定存在?
因为p节点为红,假设祖父节点不存在,那么p将称为整个树的根,违反了性质2(根节点为黑色)。
这种情况下,为了让以g为跟的子树不存在连续的红色节点,并且不让增加路径上的黑色节点,我们让g的颜色变红,让u和p的颜色变黑,各路径上黑色节点数量没变,依次达到红黑树性质。
因为p和u的颜色变黑,对子树没有影响,但是g的颜色变红,可能存在影响,需要继续向上判断:
- 如果g为该树的根,那么最后需要将g的颜色变黑
- 如果g是子树,如果g的父节点是黑色节点则不用调整,如果是红色节点则需要根据情况来做调整。
④cur为红,u为黑(存在为黑或者不存在),p为g的左孩子,cur为p的左孩子(或者p为g的右孩子,cur为p的左孩子),就是同向的。u的情况有两种:
1、如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。
2、如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的,现在看到其是红色的原因为cur的子树在调整的过程中将cur节点的颜色由黑色变为红色。
在此情况下,需要将p、g变色,p、g变换即左单旋(或者右单旋)
代码:(这里的parent为g)
//右单旋
void RotateR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right; //subL的右边可能为nullptr
Node* pparent = parent->_parent;
//将subLR连接到parent的左边
parent->_left = subLR;
if (subLR != nullptr) {
subLR->_parent = parent;
}
//将parent连接到subL的右侧
subL->_right = parent;
parent->_parent = subL;
//将subL和pparent连接起来
//pparent为nullptr,subL要成为新的根
if (pparent == nullptr) {
subL->_parent = pparent;
_root = subL;
}
else {
if (pparent->_left == parent) {
pparent->_left = subL;
}
else {
pparent->_right = subL;
}
subL->_parent = pparent;
}
}
//左单旋
void RotateL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left; //subR的左边可能为nullptr
Node* pparent = parent->_parent;
parent->_right = subRL;
if (subRL != nullptr) {
subRL->_parent = parent;
}
subR->_left = parent;
parent->_parent = subR;
if (pparent == nullptr) {
subR->_parent = pparent;
_root = subR;
}
else {
if (pparent->_right == parent) {
pparent->_right = subR;
}
else {
pparent->_left = subR;
}
subR->_parent = pparent;
}
}
⑤cur为红,p为红,u为黑(存在为黑或者不存在),p为g的左孩子,cur为p的右孩子(或者p为g的右孩子,cur为p的左孩子;反正两者相反)
此处为右左单旋:
红黑树的验证:
红黑树的验证分为两步:
- 检测是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测是否满足红黑树的性质
bool IsRBTree() {
//空树
if (_root == nullptr) {
return true;
}
//根节点为黑色
if (_root->_cor == RED) {
cout << "根节点为红色" << endl;
return false;
}
//黑色节点数量相同,先走一条得到基准值
int blacknum = 0;
Node* cur = _root;
while (cur) {
if (cur->_cor == BLACK) {
blacknum++;
}
cur = cur->_left;
}
//检查子树
return _IsRBTree(_root, blacknum, 0);
}
bool _IsRBTree(Node* root, int blacknum, int count) {
//递归到空节点
if (root == nullptr) {
if (blacknum == count) {
return true;
}
else{
cout << "各路径黑色节点个数不同" << endl;
return false;
}
}
//子节点为红,则检查父节点是否为红
if (root->_cor == RED && root->_parent->_cor == RED) {
cout << "存在连续红节点" << endl;
return false;
}
//计数黑节点
if (root->_cor == BLACK) count++;
//递归左右子树
return _IsRBTree(root->_left, blacknum, count) && _IsRBTree(root->_right, blacknum, count);
}
与AVL树的比较
- 都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN)
- 红黑树不追求绝对的平衡,相对而言,降低了插入和旋转的次数
- 所以再经常进行增删的结构中性能更好,而且实现比AVL树简单。
代码:
#pragma once
#include<iostream>
using namespace std;
enum Color {
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode {
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
pair<K, V> _kv;
Color _cor;
RBTreeNode(const pair<K, V>& kv)
:_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
,_kv(kv)
,_cor(RED){}
};
template<class K, class V>
class RBTree {
public:
typedef RBTreeNode<K, V> Node;
RBTree()
:_root(nullptr){}
bool Insert(const pair<K, V>& kv) {
//_root为nullptr,说明是插入的第一个值
if (_root == nullptr) {
_root = new Node(kv);
_root->_cor = BLACK;
}
Node* parent = nullptr;
Node* cur = _root;
//查找插入位置
while (cur) {
if (kv.first < cur->_kv.first) {
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first) {
parent = cur;
cur = cur->_right;
}
else {
return false;
}
}
cur = new Node(kv);
if (kv.first > parent->_kv.first) {
parent->_right = cur;
cur->_parent = parent;
}
else {
parent->_left = cur;
cur->_parent = parent;
}
//控制平衡
while (parent != nullptr && parent->_cor == RED) {
//不需要判断greadfather是否为nullptr,因为parent如果是红色,不可能是根
Node* grandfather = parent->_parent;
if (parent == grandfather->_left) {
Node* uncle = grandfather->_right;
//情况一:uncle存在且为红,不需要旋转处理
if (uncle && uncle->_cor == RED) {
//变色 + 向上继续调整
parent->_cor = BLACK;
uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = grandfather->_parent;
}
//情况二:uncle存在且为黑/不存在
else {
//cur为parent的左孩子,需要进行右单旋
if (cur == parent->_left) {
RotateR(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
//cur为parent的右孩子,需要左右双旋
else {
RotateLR(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
//不用再往上更新
break;
}
}
//parent为grandfather的右孩子
else {
Node* uncle = grandfather->_left;
//情况一:uncle存在且为红,不需要旋转处理
if (uncle != nullptr && uncle->_cor == RED) {
//变色 + 向上调整
parent->_cor = uncle->_cor = BLACK;
grandfather->_cor = RED;
cur = grandfather;
parent = cur->_parent;
}
//情况二:uncle存在且为黑/不存在
else {
//cur为parent的右孩子,需要进行左单旋
if (cur == parent->_right) {
RotateL(grandfather);
parent->_cor = BLACK;
grandfather->_cor = RED;
}
//为左孩子,进行右左双旋
else {
RotateRL(grandfather);
cur->_cor = BLACK;
grandfather->_cor = RED;
}
break;
}
}
//保证根节点为黑
_root->_cor = BLACK;
return true;
}
}
bool IsRBTree() {
//空树
if (_root == nullptr) {
return true;
}
//根节点为黑色
if (_root->_cor == RED) {
cout << "根节点为红色" << endl;
return false;
}
//黑色节点数量相同,先走一条得到基准值
int blacknum = 0;
Node* cur = _root;
while (cur) {
if (cur->_cor == BLACK) {
blacknum++;
}
cur = cur->_left;
}
//检查子树
return _IsRBTree(_root, blacknum, 0);
}
bool _IsRBTree(Node* root, int blacknum, int count) {
//递归到空节点
if (root == nullptr) {
if (blacknum == count) {
return true;
}
else{
cout << "各路径黑色节点个数不同" << endl;
return false;
}
}
//子节点为红,则检查父节点是否为红
if (root->_cor == RED && root->_parent->_cor == RED) {
cout << "存在连续红节点" << endl;
return false;
}
//计数黑节点
if (root->_cor == BLACK) count++;
//递归左右子树
return _IsRBTree(root->_left, blacknum, count) && _IsRBTree(root->_right, blacknum, count);
}
private:
Node* _root;
//右单旋
void RotateR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right; //subL的右边可能为nullptr
Node* pparent = parent->_parent;
//将subLR连接到parent的左边
parent->_left = subLR;
if (subLR != nullptr) {
subLR->_parent = parent;
}
//将parent连接到subL的右侧
subL->_right = parent;
parent->_parent = subL;
//将subL和pparent连接起来
//pparent为nullptr,subL要成为新的根
if (pparent == nullptr) {
subL->_parent = pparent;
_root = subL;
}
else {
if (pparent->_left == parent) {
pparent->_left = subL;
}
else {
pparent->_right = subL;
}
subL->_parent = pparent;
}
}
//左单旋
void RotateL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left; //subR的左边可能为nullptr
Node* pparent = parent->_parent;
parent->_right = subRL;
if (subRL != nullptr) {
subRL->_parent = parent;
}
subR->_left = parent;
parent->_parent = subR;
if (pparent == nullptr) {
subR->_parent = pparent;
_root = subR;
}
else {
if (pparent->_right == parent) {
pparent->_right = subR;
}
else {
pparent->_left = subR;
}
subR->_parent = pparent;
}
}
//右左双旋
void RotateRL(Node* parent) {
RotateR(parent->_right);
RotateL(parent);
}
//左右双旋
void RotateLR(Node* parent) {
RotateR(parent->_left);
RotateL(parent);
}
};
int main() {
RBTree<int, int> t;
t.Insert(make_pair(1, 1));
t.Insert(make_pair(2, 2));
t.Insert(make_pair(3, 3));
if (t.IsRBTree()) {
cout << "T" << endl;
}
return 0;
}