红黑树
一:红黑树简介
1.概念
- 红黑树
是一种特殊的二叉搜索树,但是每一个结点上增加一个存储位表示结点的颜色,可以是Red 或着 Black.通过对任何一条从根到叶子的路径上各个着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而接近平衡
2.红黑树的性质
相比于平衡二叉树,去掉了平衡因子这种保持平衡的方式,而引入了新的机制(用树中结点的颜色来维持平衡).
-
1.每个结点不是红色就是黑色
-
2.根节点都是黑色的
-
3.如果一个结点是红色的,则它的两个孩子结点是黑色,如果一个结点时黑色的,那么它的子结点没有规律
-
4.对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
-
5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
-
根据规则4:新增的结点必须是红色的,
-
根据规则3:新增的结点之父结点必须是黑色的
-
当新的结点根据二叉树的规则到达其插入点,却未符合上述条件时,就必须调整颜色并旋转树形
注意:叶子结点不一定为红,可以为黑(那是调整过后的结果)
3.红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中值最小的节点,_pRight域指向红黑树中值最大的节点
- 红黑树示意图
- 结点结构体的定义
enum Color{
Red,
Black
};
//用枚举的方式标识结点的颜色
//用0代表红色,用1代表黑色
template <class T>
struct RBTNode{
RBTNode(const T& kv=T(),Color color=Red)
:_kv(kv)
,_color(color)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
{
}
T _kv;
Color _color;
RBTNode<T>* _left;
RBTNode<T>* _right;
RBTNode<T>* _parent;
};
二:红黑树的插入
-
1.按照二叉搜索树的规则插入新的结点
-
2.检测新结点插入完成之后,红黑树的性质是否遭到破坏
-
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;
-
但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前插入的结点,p为父结点,g为祖父结点,u为叔叔结点
情况1:cur为红,p为红,g为黑,u存在且为红
- 图示
- 解决办法
将p,u改为黑,g改为红,然后g 把当成cur,继续向上调整
情况2:cur为红,p为红,g为黑,u不存在/u为黑
1.图示(u不存在)
- 解决办法
- 以g为轴进行右单旋,并将g变红p变黑
2.图示(u存在且为黑色,p为g的左孩子,cur为p的左孩子)
- 解决办法
- p为g的左孩子,cur为p的左孩子,则进行右单旋转;
- p、g变色–p变黑,g变红
3.图示(p为g的右孩子,cur为p的右孩子)
- 解决办法:
- 相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
- p、g变色–p变黑,g变红
情况3:cur为红,p为红,g为黑,u不存在/u为黑
1.图示(p为g的左孩子,cur为p的右孩子)
- p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,再针对g做右单旋,并g变红,p变黑.此时的cur为图中树的根结点,但是cur如果不是_head->_parent则继续调整,若cur是_head->_parent则将cur变黑(红黑树的根结点颜色为黑),结束调整
2.图示(p为g的右孩子,cur为p的右孩子)
- 相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,再针对g做左单旋,并g变红,p变黑.此时的cur为图中树的根结点,但是cur如果不是_head->_parent则继续调整,若cur是_head->_parent则将cur变黑(红黑树的根结点颜色为黑),结束调整
一个由上而下的程序
- 为了避免"父子节点皆为红色"的情况持续向RB-tree的上层结构发展,形成处理时失效上的瓶颈,我们可以施行一个由上而下的程序(top-downprocedure):假设新增结点为A,那么就沿着A的路径,只要看到有某节点X的两个子节点为红色,就把X改为红色,并把两个子结点改为黑色,
- 如果X的父节点P亦为红色(注意:此时S绝不可能为红),需要做一次单旋并改变颜色.
- 此后,结点35的插入就很单纯了,要么直接插入,要么插入之后(若X结点为红)再一次旋转(单双皆可以)即可
三:红黑树的整体实现
#pragma once
#include<iostream>
#include<functional>
using namespace std;
enum Color{
Red,
Black
};
template <class T>
struct RBTNode{
RBTNode(const T& kv=T(),Color color=Red)
:_kv(kv)
,_color(color)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
{
}
T _kv;
Color _color;
RBTNode<T>* _left;
RBTNode<T>* _right;
RBTNode<T>* _parent;
};
template <class T>
class RBTree{
public:
typedef RBTNode<T> Node;
typedef RBTNode<T>* pNode;
RBTree()
:_head(new Node)
{
_head->_left=_head;
_head->_right=_head;
}
bool Insert(T& kv){
if(_head->_parent==nullptr){
//第一次插入的时候插入的是根结点,且颜色为黑色
pNode _root=new Node;
_root->_kv=kv;
_root->_color=Black;
_root->_parent=_head;
_head->_left=_root;
_head->_right=_root;
_head->_parent=_root;
return true;
}
//处理存在根结点的情况
pNode cur=_head->_parent;
pNode parent=nullptr;
while(cur){
parent=cur;
if(cur->_kv > kv){
cur=cur->_left;
}else if(cur->_kv < kv){
cur=cur->_right;
}else{
return false;
}
}
//进行插入
cur=new Node;
cur->_kv=kv;
if(parent->_kv>cur->_kv){
parent->_left=cur;
}else{
parent->_right=cur;
}
cur->_parent=parent;
//进行调整变色
while(cur!=_head->_parent && cur->_parent->_color==Red){
pNode parent=cur->_parent;
pNode gparent=parent->_parent;
if(gparent->_left==parent){
pNode uncle=gparent->_right;
//u存在且为红
if(uncle && uncle->_color==Red){
//情况一
parent->_color=Black;
uncle->_color=Black;
gparent->_color=Red;
cur=gparent;
}else{
//u不存在 / 存在且为黑
//1.以g为轴右旋
//2.调整颜色,p为黑色,g为红色
//检查是否为双旋场景:左右双旋
if(parent->_right==cur){
RotateL(parent);
swap(parent,cur);
}
RotateR(gparent);
parent->_color=Black;
gparent->_color=Red;
break;
}
}else{
pNode uncle=gparent->_left;
if(uncle && uncle->_color==Red){
parent->_color=uncle->_color=Black;
gparent->_color=Red;
cur=gparent;
}else{
//u存在且为黑/u不存在
if(parent->_left==cur){
RotateR(parent);
swap(cur,parent);
}
RotateL(gparent);
gparent->_color=Red;
parent->_color=Black;
break;
}
}
}
//红黑树根始终是黑色的
_head->_parent->_color=Black;
//为了实现后续的迭代器,将head的左右指针指向最大,最小的值
_head->_left=leftMost();
_head->_right=rightMost();
return true;
}
pNode leftMost(){
pNode cur=_head->_parent;
while(cur && cur->_left){
cur=cur->_left;
}
return cur;
}
pNode rightMost(){
pNode cur=_head->_parent;
while(cur && cur->_right){
cur=cur->_right;
}
return cur;
}
void RotateL(pNode parent){
pNode subR=parent->_right;
pNode subRL=subR->_left;
subR->_left=parent;
parent->_right=subRL;
if(subRL){
subRL->_parent=parent;
}
if(parent!=_head->_parent){
pNode gparent=parent->_parent;
//判断parent之前是parent->_parent 的那一边的结点
//把subR链接到对应的边
if(gparent->_left==parent)
gparent->_left=subR;
else
gparent->_right=subR;
//更新subR的parent
subR->_parent=gparent;
}
else {
//如果parent是根,subR变成新的根
subR->_parent=nullptr;
_head->_parent=subR;
}
//链接subR与parent
parent->_parent=subR;
}
void RotateR(pNode parent){
pNode subL=parent->_left;
pNode subLR=subL->_right;
//1.单向链接subL,subLR,parent
subL->_right=parent;
parent->_left=subLR;
//2.向上链接subLR,parent
if(subLR){
subLR->_parent=parent;
}
//3.双向链接subL与parent<_parent
if(parent!=_head->_parent){
pNode gParent=parent->_parent;
if(gParent->_left==parent){
gParent->_left=subL;
}else{
gParent->_right=subL;
}
//把subL的父亲结点改成其祖宗结点
subL->_parent=gParent;
}else{
subL->_parent=nullptr;
_head->_parent=subL;
}
//4.向上链接parent,subL
parent->_parent=subL;
}
void Inorder(){
_Inorder(_head->_parent);
cout<<endl;
}
bool IsValidRBTree()
{
pNode pRoot = GetRoot();
// 空树也是红黑树
if (nullptr == pRoot)
return true;
// 检测根节点是否满足情况
if (Black != pRoot->_color)
{
cout << "违反红黑树性质一:根节点必须为黑色!" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackCount = 0;
pNode pCur = pRoot;
while (pCur)
{
if (Black == pCur->_color)
blackCount++;
pCur = pCur->_left;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
return _IsValidRBTree(pRoot, k, blackCount);
}
private:
void _Inorder(pNode parent){
if(parent){
_Inorder(parent->_left);
cout<<parent->_kv<<" ";
_Inorder(parent->_right);
}
}
pNode GetRoot(){
return _head->_parent;
}
bool _IsValidRBTree(pNode pRoot, size_t k, const size_t blackCount) {
//走到null之后,判断k和black是否相等
if (nullptr == pRoot)
{
if (k != blackCount)
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
return true;
}
// 统计黑色节点的个数
if (Black == pRoot->_color)
k++;
// 检测当前节点与其双亲是否都为红色
pNode pParent = pRoot->_parent;
if (pParent && Red == pParent->_color && Red == pRoot->_color)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
return _IsValidRBTree(pRoot->_left, k, blackCount) &&
_IsValidRBTree(pRoot->_right, k, blackCount);
}
private:
pNode _head;
};