目录
什么是红黑树?
红黑树是一种特化的AVL树(平衡二叉树),它的结点由红色结点和黑色结点组成
为什么要引入红黑树?
平衡二叉树可以防止最坏的二叉查找树生成,让时间复杂度维持在O(logN),大大提高了二叉查找树的效率,但是平衡二叉树的插入、删除需要多次的旋转来维持平衡,维护代价太大。于是我们引入红黑树来代替平衡二叉树。红黑树和平衡二叉树效率相差不大(尤其在数据小的时候),且比平衡二叉树维护代价小。
红黑树算是平衡二叉树和二叉查找树的一种“折中”方法
ps.STL中的容器map、set的底层都是红黑树
红黑树的性质
- 每一个结点不是黑色就是红色
- 根节点必须是黑色
- 红色结点的子节点必须是黑色的(红色结点不能连续,黑色可以)
- 每个nil结点是黑色的(nil结点:可以当成null的代称)
- 每一个路径上包含相同数目的黑色结点(从根节点到nil结点为一条路径!)
因为红黑树保持上述性质,所以红黑树能做到确保没有一条路径会比其他路径长出两倍
下面就是一颗红黑树:
(nil其实就是null结点,画了一个方便理解)
红黑树的旋转:关键看“叔叔”
判断红黑树是否的旋转可以大致分为以下两种情形:
- 有叔叔结点且叔叔结点为红色:变色
- 没有叔叔结点或叔叔结点为黑色:旋转+变色
(左旋右旋的原理在这里不做赘述)
让我们放大来看这两个情形:
(设在左子树中新插入一个结点)
叔叔存在且叔叔为红色:
叔叔不存在或叔叔为黑色:
1.在左子树的左边结点插入:
2.在左子树的右边结点插入:
这一部分的代码实现如下:
(左旋和右旋的代码附在最后)
//关键看叔叔
if (parent == grandfather->_left)
{
node* 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);
parent->_col = Black;
grandfather->_col = Red;
}
左右单旋+变色
else
{
RotateL(parent);
RotateR(grandfather);
grandfather->_col = Red;
cur->_col = Black;
}
break;
}
}
(如果在右子树中插入新结点就是左子树的反向,这里就直接附代码了,可以自己对比理解一下)
else //看右边去
{
node* 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);
parent->_col = Black;
grandfather->_col = Red;
}
else
{
RotateR(parent);
RotateL(grandfather);
grandfather->_col = Red;
cur->_col = Black;
}
break;
}
}
}
思维导图
于是可以总结出一个关于红黑树旋转过程的思维导图:
思维导图放大版:
完整源码
插入时红黑树调整的代码:
//红黑树调整
while (parent && parent->_col == Red)
{
node* grandfather = parent->_parent;
assert(grandfather);
assert(grandfather->_col == Black);
//关键看叔叔
if (parent == grandfather->_left)
{
node* 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);
parent->_col = Black;
grandfather->_col = Red;
}
左右单旋+变色
else
{
RotateL(parent);
RotateR(grandfather);
grandfather->_col = Red;
cur->_col = Black;
}
break;
}
}
else //看右边去
{
node* 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);
parent->_col = Black;
grandfather->_col = Red;
}
else
{
RotateR(parent);
RotateL(grandfather);
grandfather->_col = Red;
cur->_col = Black;
}
break;
}
}
}
//调整结束后令根节点为黑色
_root->_col = Black;
左旋右旋的代码:
//右旋
void RotateR(node* parent)
{
node* SubL = parent->_left;
node* SubLR = SubL->_right;
parent->_left = SubLR;
if (SubLR)
SubLR->_parent = parent;
node* pparent = parent->_parent;
SubL->_right = parent;
parent->_parent = SubL;
if (_root == parent)
{
_root = SubL;
SubL->_parent = nullptr;
}
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;
parent->_right = SubRL;
if (SubRL)
SubRL->_parent = parent;
node* pparent = parent->_parent;
SubR->_left = parent;
parent->_parent = SubR;
if (_root == parent)
{
_root = SubR;
SubR->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = SubR;
}
else
{
pparent->_right = SubR;
}
SubR->_parent = pparent;
}
}