目录
1、cur为红,parent为红,grandfather为黑,uncle存在且为红
2、cur为红,parent为红,grandfather为黑,uncle不存在或者存在且为黑(单旋+变色)
3、cur为红,parent为红,grandfather为黑,uncle不存在或者存在且为黑(双旋+变色)
一、红黑树的性质
1、什么是红黑树
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。
2、红黑树的性质
1. 每个结点不是红色就是黑色2. 根节点是黑色的3. 如果一个节点是红色的,则它的两个孩子结点是黑色的4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色点5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树的规则保证了,最长路径中的节点个数不会超过最短路径的2倍
极端情况下:
最短路径全是黑色节点
最长路径一黑一红交替出现
我们只要保证了红黑树的规则就保证了,最长路径中的节点个数不超过最短路径节点个数的2倍
二、红黑树节点的定义
enum Colour
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<const K, V> _kv;
Colour _col;
RBTreeNode(const pair<const K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
{}
};
三、红黑树的插入
假如我们要插入28这个节点,我们一定要插入到27的右边,那么我们新插入的节点的颜色设定为什么呢?
如果设定为红色那么就是与 规则3相悖,如果设定为黑色就是与规则4相悖
既然无论如何都要相悖,那么我们选择与规则3相悖,因为如果是在11,15后面插入时,直接插入红色节点,就不用再处理其它节点了
如果插入黑色节点,我们就要遍历整棵树来调整黑色节点的个数
bool insert(const pair<const K, V> kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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
{
assert(false);
}
}
cur = new Node(kv);
cur->_parent = parent;
//调整节点颜色
return true;
}
1、cur为红,parent为红,grandfather为黑,uncle存在且为红
这是最简单的情况了
我们让parent和uncle变成黑色,grandfather变为红色,如果grandfather是根就再将其变为黑色
2、cur为红,parent为红,grandfather为黑,uncle不存在或者存在且为黑(单旋+变色)
3、cur为红,parent为红,grandfather为黑,uncle不存在或者存在且为黑(双旋+变色)
bool insert(const pair<const K, V> kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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);
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);
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
{
/* g
* p
* c
*/
if (cur == parent->_left)
{
RotateR(parent);
parent->_col = BLACK;
grandfather->_col = RED;
}
/* g
* p u
* c
*/
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
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
{
/* g
* p
* c
*/
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
/* g
* u p
* c
*/
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
}
}
}
_root->_col = BLACK;
return true;
}
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;//记录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;
}
}
四、红黑树的验证
红黑树的验证主要是根据它的规则来验证,不能使用最长路径是最短路径的二倍
因为最短路径是最长路径的二倍的树不一定是红黑树
所以我们要使用它的条件来判断
1. 每个结点不是红色就是黑色2. 根节点是黑色的3. 如果一个节点是红色的,则它的两个孩子结点是黑色的4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色点5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
第一点因为使用的是枚举类型,所以很容易保证
第二点只要验证根不为红色就可以
第三点是前序遍历,判断是否具有连续的红色节点
第四点是使用一个计数器,随便记录一条路的黑色节点个数,然后对比
bool IsBalance()
{
int benchmark = 0;
return _PrevCheck(_root, 0, benchmark);
}
bool _PrevCheck(Node* root, int blackNum, int& benchmark)
{
if (root == nullptr)
{
if (benchmark == 0)
{
benchmark = blackNum;
return true;
}
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);
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了红黑树的基本结构和插入操作。