RB-tree概述
红黑树是平衡二叉搜索树的一种,其通过特定的操作来保持二叉查找树的平衡。
二叉搜索树是指一个空树或者具有以下性质的二叉树:
- 任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点。
红黑树与AVL树的比较:
- AVL树的时间复杂度虽然优于红黑树,但是对于现在的计算机,cpu太快,可以忽略性能差异。
- 红黑树的插入删除比AVL树更便于控制操作。
- 红黑树整体性能略优于AVL树(红黑树旋转情况少于AVL树)。
红黑树的性质
红黑树是一棵二叉搜索树,它在每个节点增加了一个存储位记录节点的颜色,可以是RED或BLACK;通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡。
具体性质如下:
- 每个节点颜色不是黑色,就是红色
- 根节点是黑色的
- 如果一个节点是红色,那么它的两个子节点就是黑色的(没有连续的红节点)
- 对于每个节点,从该节点到其后代叶节点的简单路径上,均包含相同数目的黑色节点
那么为什么当满足以上性质时,就能保证最长路径不超过最短路径的二倍了呢?我们分析一下:
你的最短路径就是全黑节点,最长路径就是一个红节点一个黑节点,最后黑色节点相同时,最长路径刚好是最短路径的两倍。
红黑树的插入
红黑树插入节点过程大致分析:
RBTree为二叉搜索树,我们按照二叉搜索树的方法对其进行节点插入,RBTree有颜色约束性质,因此我们在插入新节点之后要进行颜色调整。
具体步骤如下:
- 根节点为NULL,直接插入新节点并将其颜色置为黑色
- 根节点不为NULL,找到要插入新节点的位置
- 插入新节点
- 判断新插入节点对全树颜色的影响,更新调整颜色
首先红黑树的插入其实不是那么容易实现的,我们需要思考一个问题,你插入节点的默认颜色是RED或BLACK?
这里我们需要根据性质来思考,首先如果插入黑节点,这个可以直接插入无论它的父亲是什么颜色,但是红黑树的性质是每条路径的黑色节点数目相同,这个时候你再想想那其他路径的黑色节点数目一定比你现在少一个节点,所以调整起来是非常繁琐的。插入红节点不需要调整其他路径,如果它的父亲为黑,那么直接插入,如果他的父亲为红那么在该路径上面开始分情况调整。
所以插入节点默认颜色一定要为红.如果为黑调节成本太大了。
接下来开始插入节点如果插入节点的父亲为黑那么直接插入后返回不需要做任何调整。但是如果插入节点的父亲为红,那么就需要调整了。具体的调整过程可以分为三个情况:
- 第一种情况
cur为红,parent为红,pParent为黑,uncle存在且为红
则将parent, uncle改为黑,pParent改为红,然后把pParent当成cur,继续向上调整。
此时对该子树进行操作,parent节点和uncle节点变为黑,pParent节点变为红,这样我们就保证该子树中每条路径中黑色节点相同并且没有连续的红节点,然后再让cur等于pParent节点继续往上继续调整。
2. 第二种情况
cur为红,parent为红,pParent为黑,uncle不存在/u为黑
parent为pParent的左孩子,cur为parent的左孩子,则进行右单旋转;
相反,parent为pParent的右孩子,cur为parent的右孩子,则进行左单旋转;
p、g变色–p变黑,g变红.
3. 第三种情况
cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转
则转换成了情况2
相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2 在这里插入图片描述
红黑树插入节点代码实现如下:
bool Insert(const K& key, const V& value)
{
//根节点为空
if (_root == NULL)
{
_root = new Node(key,value);
_root->_color = BLACK;
return true;
}
//根节点不为空
//找到新节点插入位置
Node* parent = NULL;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key >key)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
//插入新节点
cur = new Node(key, value);
cur->_color = RED;
if (parent->_key > key)
{
parent->_left = cur;
cur->_parent = parent;
}
else//parent->_key < key
{
parent->_right = cur;
cur->_parent = parent;
}
//插入节点后颜色的调整
while (parent && parent->_color == RED)
{
Node* grandfather = parent->_parent;//grandfather颜色一定为黑色
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//uncle存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//uncle不存在/uncle存在且为黑
{
if (cur == parent->_right)
{
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
else//grandfather->_right==parent
{
Node* uncle = grandfather->_left;
//uncle存在且为红
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//不存在/存在且为黑
{
if (cur == parent->_left)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
}//end while (parent && parent->_color == RED)
_root->_color = BLACK;
return true;
}