🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
文章目录
- 一、红黑树概念
- 二、红黑树性质
- 三、红黑树 插入
- ①变色(c红 p红 g黑 u存在且红)
- ②旋转(c红 p红 g黑 u存在且黑/不存在)
- ③双旋转(c红 p红 g黑 u存在且黑/不存在)
- 四、旋转代码
- 五、红黑树插入代码
- 六、红黑树平衡判断
红黑树认为,AVL数控制的太严格,越严格就会有越多的旋转
AVL树严格平衡,红黑树近似平衡
这样的话红黑树也会造成一些不好的后果,比如查找某些数据次数多一些,略慢一些,但是大部分情况还是在中间就找到了,所以查找效率大差不差,对于CPU而言,没什么差别,但是红黑树能具有更少的旋转!!!
一、红黑树概念
红黑树,是一种搜索二叉树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因为是接近平衡的。
二、红黑树性质
1.每个结点不是黑色就是红色
2.根结点是黑色
3.不存在连续两个红色的结点
4.每条路径的黑色结点数目相同
5.每个叶子都是黑色结点(指的是空结点NIL),加上NIL结点就是方便画图数路径
这样如何保证最长路径不超过最短路径的两倍呢?
极限最短:全黑
极限最长:一黑一红
三、红黑树 插入
现在要新插入结点,插入什么颜色?
如果是黑色,那么这一条路径的黑色路径和其他路径都不同了
如果是红色,那还好说,只有可能是该路径上的上一个和自己不同
红黑树被影响了之后有两个方向
1.变色
2.旋转
如果在27右边插入一个结点,那么27一定要先变黑,不然的话,就违背不能连续两个红,而此时如果不变27,而去变新插入的颜色,那又违背每条路径黑色结点数量相等的原理。
红黑树遇事不决看叔叔
新插入结点 | cur |
---|---|
新插入结点的父亲27 | parent |
新插入结点的叔叔22 | uncle |
父亲的父亲 | grandfather |
①变色(c红 p红 g黑 u存在且红)
具象图:
第一步,parent变黑,uncle一起变黑,因为右边多了一个黑,uncle来平衡左右,然后grandfather变红,一直往上走,到为止,然后根变黑即可
也就是说,这种情况只需要没条路径都多一个黑结点就可以了
有些情况单单变色解决不了
比如事先就有一个uncle结点是黑色,无法向这条路径增添多的黑,此时,就需要分情况来进行旋转了,需要注意的是新增添的结点一定是红,因为为了不破坏每条路径黑色结点数量相同的原理
②旋转(c红 p红 g黑 u存在且黑/不存在)
Ⅰ、如果u不存在,那么cur就是新增,因为cur一定是红,此时如果不是新增,就违背了不能两个连续红,不存在的时候旋转也不碍事
Ⅱ、uncle存在且为黑,这时cur一定为黑,那么现在cur为红的原因是子树因为颜色的调整,把cur变红
这时,uncle是黑色,父亲无法变色,不然的话就这条路径多了一个黑,所以这种情况就需要旋转:把grandfather旋转下来变成红色,parent变黑
子树中还有其他黑色结点,不用担心
③双旋转(c红 p红 g黑 u存在且黑/不存在)
同理,uncle不存在,cur即为新增,就算不存在,我旋转以下也不碍事
此时如果对p右单旋,再进行刚刚的变色操作的话,就会让右边多两个黑色结点,因为黑色结点在cur的字数中,而现在因为单旋让cur到了右树
所以这里的正确操作时需要双旋,首先将cur旋转到左树上去,也就是对p左单旋,转变为情况二进行单旋
四、旋转代码
//左旋
void RotateL(Node*parent)
{
Node*subR = parent->_right;
Node*subRL = subR->_left;
parent->_right = subRL;
if(subRL)
{
subRL->_parent = parent;
}
Node*ppNode=parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if(_root==parent)
{
_root=subR;
subR->_parent=nullptr;
}
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)
{
subLR->_parent = parent;
}
Node*ppNode=parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if(_root==parent)
{
_root=subL;
subL->_parent=nullptr;
}
else
{
if(ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
subL->_parent = ppNode;
}
}
五、红黑树插入代码
bool Insert(const pair<K,V>&kv)
{
if(_root==nullptr)
{
_root=new Node(kv);
_root->col =BLACK;//enum,根结点为黑
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);//传键值对new
cur->_col=RED;//新插入一定是red
//看插入哪边
if(parent->_kv.first<kv,first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur -> _parent = parent;
//红黑树的三种情况的处理
while(parent&&parent->_col==RED)//需要继续
{
Node*grandfather = parent->_parent;
assert(grandfather&&grandfather0>_col==BLACK);
//不为nullptr且不能和parent一起红
Node*uncle;
if(parent==grandfather->left)
{
uncle == grandfather->_right;
if(uncle&&uncle->_col==RED)
{//情况一,变色即可
parent->_col=uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上
cur = grandfather;
parent=cur->_parent;
}
else//情况二三
{
if(cur == parent->_left)
{
RotateR(grandfather);//右旋
grandfather->_col=RED;
parent->_col= BLACK;
}
else if(cur==parent->_right)
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather ->_col= RED;
}
}
}
else if(parent==grandfather->_right)
{
//相同代码,只是换了一边
uncle = grandfather->_left;
if(uncle&&uncle->_col==RED)
{
parent->_col = uncle->_col=BLACK;
grandfather->_col = RED;
cur=grandfather;
parent=cur->_parent;
}
else
{
if(cur==parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else if(cur == parent->_left)
{
RotateR(parent);
RotateL(grandfather);
cur->_col=BLACK;
grandfather->_col = RED;
}
}
}
}
_root->_col = BLACK;
return true;
}
六、红黑树平衡判断
红黑树最长路径不超过最短路径的二倍是颜色互斥得来的,所以判断颜色是否符合要求一定靠谱。
检查
1.每条简单路径的黑色结点数量相同
2.不能连续的红色结点
3.根结点是黑色
bool IsBalance()
{
if(_root==nullptr)
return true;
if(_root->_col==RED)
{
cout<<"根结点不是黑色"<<endl;
return false;
}
int benchMark=0;
//黑色结点数量基准值,随便选一条
Node*cur =_root;
while(cur)
{
if(cur>_col == BLACK)
++benchMark;
cur = cur->_left;
}
return PrevCheck(_root,0,benchMark);
}
bool PrevCheck(Node*root,int blackNum,int benchMark)
{
if(root==nullptr)
{
if(blackNum!=benchMark)
{
cout<<"某条黑色结点数量不相等"<<endl;
return false;
}
else
{
return true;
}
}
if(root->_col==BLACK)
{
++blackNum;
}
if(root->_col==RED&&root->_parent->_col==RED)
{
cout<<"存在连续红色结点"<<endl;
return false;
}
return PrevCheck(root->_left,blackNum,benchMark)
&&PrevCheck(root->_right,blackNum,benchMark);
}