一、红黑树的性质
红黑树是一种接近平衡的二叉搜索树,在上一篇博客AVL树中说过,AVL树是通过平衡因子来控制平衡的,右子树与左子树高度差的绝对值不超过1,而红黑树则是通过颜色来实现近似平衡的,在红黑树中,最长路径结点个数不超过最短路径结点个数的两倍。
红黑树的性质:
1)每个结点不是红色就是黑色
2)根结点为黑色
3)如果一个结点是红色的,则它的孩子是黑色的
4)对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
5)每个叶子结点(NIL结点)都是黑色的
红黑树,如其名,是由一个个红色结点和黑色结点组成的,注意它的根结点必须是黑色的。关于第三点,通俗点说就是不能有连续的红色结点,但是可以有连续的黑色结点。关于第五点,这里的NIL结点是指空结点。
下面给一颗红黑树的示例:
思考:为什么 红黑树能保证最长路径结点个数不超过最短路径结点个数的两倍?
可以想象一下,每条路径的黑色结点数量是一样的,最极端的情况下,最短路径应该是全是黑色结点,最长路径为黑色结点与红色结点交替,这样最长路径结点数是最短路径结点数的两倍,而一般情况下是很少有一条路径中全是黑色结点的,所以,最长路径结点数不超过最短路径结点数的两倍。
二、红黑树的插入
红黑树与AVL数非常类似,插入时也会涉及到旋转,有了前边AVL树的基础,红黑树的插入就相对简单多了,接下来详细讲解红黑树的插入操作。
结点插入的位置与之前二叉搜索树一样,遍历红黑树,小就走左子树,大就走右子树,最终到要插入的位置,需要考虑的是结点应该给什么颜色,根据红黑树的性质,每条路径的黑色结点数量是一致的,若是新插入的结点给红色,那么其他路径黑色结点数量就要增加,这显然很麻烦,所以新增结点给红色。这时候就需要注意是否会有连续的红色结点,如果说插入结点后,其父结点是黑,直接插入即可,如果其父结点为红,就违背了性质三,这时候就需要旋转了。
下边重点讲解出现连续红结点怎么旋转:
当当前结点为红色,且其父结点为红色时,有连续红结点,这时父结点的父亲一定是黑色的,否则在前边就已经违反了规则,需要考虑的就是叔叔结点的颜色,主要有两种情况,即叔叔结点也为红和叔叔结点为黑或不存在。
1)当叔叔结点也为红时,直接改变颜色即可
将parent和uncle变成黑色,再将grandfather变成红色。把grandfather变成红色后,需要继续向上调整,因为可能grandfather的父结点也为红,就又出现了连续红结点,
草图:
2)当叔叔结点不存在或者为黑色时
这种情况其实是有情况①变来的,因为变色了,所以父结点和叔叔结点颜色不一样,这时候需要旋转,与前边AVL树一样,涉及到单旋和双旋,当只是单纯一边高时,单旋即可,否则需要双旋。
①当叔叔结点不存在时
如果是单纯一边高,即parent在grandfather的左(右),cur也在parent的左(右),这时候单旋转后改变颜色即可。
草图:
将parent变黑,grandfather变红。
如果不是单纯一边高,即parent在grandfather的左(右),cur在parent的右(左),这时候要先先以parent旋转,再以grandfather旋转,最后改变颜色。
草图:
将cur变黑,grandfather变红。
②当叔叔结点为黑色时
与叔叔结点不存在的情况一样,随着grandfather移动即可,这里只给出草图。
在上面的草图中,都是以parent为grandfather的左来展开讨论的,如果parent为grandfather的右也是同理的,若是一边高单旋即可,否则则需要旋转两次,因为两种情况其实是成镜像对称的,所以颜色变化也是一样的。所以插入的代码书写的整体思路就是:先遍历树找到插入位置,再分parent为grandfather的左和右两种情况来讨论,在每种情况中再细分uncle为红色和uncle不存在或为黑两种情况(再分情况旋转一次或者两次)。
实现代码:
cpp
bool Insert(const T& data)
{
if (_pHead == nullptr)
{
_pHead = new Node(data);
_pHead->_col = BLACK; //根结点为黑
return true;
}
Node* parent = nullptr;
Node* cur = _pHead;
while (cur)
{
if (data > cur->_data)
{
parent = cur;
cur = cur->_pRight;
}
else if (data < cur->_data)
{
parent = cur;
cur = cur->_pLeft;
}
else
{
return false;
}
}
//非根结点,插入红色结点
cur = new Node(data);
cur->_col = RED;
if (data > parent->_data)
{
parent->_pRight = cur;
}
else
{
parent->_pLeft = cur;
}
cur->_pParent = parent;
//parent为红,要变色
while(parent && parent->_col == RED)
{
Node* grandfather = parent->_pParent;
//看叔叔是红还是黑,或者不存在
if (parent == grandfather->_pLeft)
{
Node* uncle = grandfather->_pRight;
if (uncle && uncle->_col == RED)
{
//uncle为也红,将parent和uncle变黑,grandfather变红
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
parent = cur->_pParent; //parent可能为空,while条件中要判断
}
else
{
//uncle存在为黑或不存在
//一边高
// grandfather(黑)
// parent(红) uncle(黑或不存在)
// cur(红)
if (cur == parent->_pLeft)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//双旋
// g
//p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; //子树根变成黑的了,不需要再向上调整了
}
}
else
{
Node* uncle = grandfather->_pLeft;
// g
//u p
// c
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
parent = cur->_pParent;
}
else
{
//uncle存在为黑或不存在
//一边高
// g
// u p
// c
if (cur == parent->_pRight)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
//双旋
// g
//u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; //子树根变成黑的了,不需要再向上调整了
}
}
}
//如果循环是因为parent为空结束,cur为根且为红,要变黑
//为了方便,直接不管什么情况,都将根变黑
_pHead->_col = BLACK;
return true;
}
最后,整体代码(左旋右旋):
cpp
enum Color
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode(const T& data = T())
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
,_col(RED)
{}
RBTreeNode<T>* _pLeft;
RBTreeNode<T>* _pRight;
RBTreeNode<T>* _pParent;
T _data;
Color _col;
};
template<class T>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
RBTree()
:_pHead(nullptr)
{}
RBTree(const T& data)
{
_pHead = new Node(data);
_pHead->_pLeft = nullptr;
_pHead->_pRight = nullptr;
_pHead->_pParent = nullptr;
}
//bool Insert(const T& data);由于上边写过,这里不给出
private:
// 左单旋
void RotateL(Node* pParent)
{
Node* subR = pParent->_pRight;
Node* subRL = subR->_pLeft; //可能为空
Node* pParentParent = pParent->_pParent;
subR->_pLeft = pParent;
pParent->_pParent = subR;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
if (pParentParent == nullptr)
{
_pHead = subR;
subR->_pParent = nullptr;
}
else
{
if (pParentParent->_pLeft == pParent)
{
pParentParent->_pLeft = subR;
}
else
{
pParentParent->_pRight = subR;
}
subR->_pParent = pParentParent;
}
}
// 右单旋
void RotateR(Node* pParent)
{
Node* subL = pParent->_pLeft;
Node* subLR = subL->_pRight;
Node* pParentParent = pParent->_pParent;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
pParent->_pParent = subL;
//连接
if (pParentParent == nullptr)
{
_pHead = subL;
subL->_pParent = nullptr;
}
else
{
if (pParentParent->_pLeft == pParent)
{
pParentParent->_pLeft = subL;
}
else
{
pParentParent->_pRight = subL;
}
subL->_pParent = pParentParent;
}
}
private:
Node* _pHead;
};