1.红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
2.红黑树的性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 (没有连续的红色节点)
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)NIF节点
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点
个数的两倍?看图
路径:从该节点到NIF节点
最短路径:全黑节点
最长路径:一黑一红
3.红黑树的平衡
节点定义
enum Color
{
RED,
BLACK,
};
template<class K,class V>
class RBNode
{
public:
typedef RBNode<K, V> Node;
RBNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _col(RED)
, _kv(kv)
{}
Node* _left;
Node* _right;
Node* _parent;
Color _col;
pair<K, V> _kv;//库里面提供的结构体,表示key和value
};
颜色初始化必须是红色,如果插入的是黑色,必定会影响每条路径的黑色节点的数量,红色的话只会影响该条路径
插入
template<class K,class V>
class RBTree
{
public:
typedef RBNode<K, V> Node;
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//...........
}
private:
Node* _root = nullptr;
};
插入分为三种情况(p为parent,g为grandparent,u为uncle)
cur一定为红色了
1:p是黑色,不用处理
2:p是红色,u存在且为红色
3:p是红色,u不存在或存在为黑色
情况2:
p是红色,u存在且为红色
变色+向上
情况3:
p是红色,u不存在或存在为黑色
u在右孩子时
u在左孩子同理
下面是插入后面实现的代码,里面有注释
while(parent&&parent->_col == RED)
{
Node* grandparent = parent->_parent;
if (grandparent->_right == parent)
{
Node* uncle = grandparent->_left;
//情况1:u存在且为红,变色加向上调整
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandparent->_col = RED;
//向上调整
cur = grandparent;
parent = cur->_parent;
}
else//情况2a:u不存在或者u存在且为黑 旋转+变色
{
/* g
u p
c*/
//左单旋+变色
if (parent->_right == cur)
{
RotateL(grandparent);
grandparent->_col = RED;
parent->_col = BLACK;
}
else
{
/* g
u p
c */
//右单旋+左单旋+变色
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandparent->_right;
//情况1:u存在且为红,变色加向上调整
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandparent->_col = RED;
//向上调整
cur = grandparent;
parent = cur->_parent;
}
else//情况2b:u不存在或者u存在且为黑 旋转+变色
{
/* g
p u
c */
//右单旋+变色
if (parent->_right == cur)
{
RotateR(grandparent);
grandparent->_col = RED;
parent->_col = BLACK;
}
else
{
/* g
p u
c */
//左单旋+右单旋+变色
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;//管你变成什么色,根反正最后还是黑色
return true;
4.红黑树的验证
验证是红黑树,就要满足他的性质,首先不能出现连续红色节点,其次每条路径的黑色节点的数量要相等
不能出现连续红色节点:不要用cur,然后和cur->孩子来比较,因为会有两个孩子,比较麻烦,可以用cur->parent来比较
每条路径的黑色节点的数量要相等:先算出一条路径的黑色节点的数量,作为基准值,进行比较
bool IsRBTree()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int nummark = 0;//给个黑色节点基准值,然后和它比较
Node* cur = _root;
while (cur)
{
if(cur->_col == BLACK)
nummark++;
cur=cur->_left;
}
return _Check(_root, 0, nummark);
}
bool _Check(Node* root, int blacknum, int nummark)
{
if (root == nullptr)
{
if (nummark != blacknum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
cout << blacknum << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
blacknum++;
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << "具有连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blacknum, nummark)
&& _Check(root->_right, blacknum, nummark);
}
void testRBTree()
{
srand(time(0));
const size_t N = 5000000;
RBTree<int, int> t;
for (size_t i = 0; i < N; ++i)
{
size_t x = rand() + i;
t.Insert(make_pair(x, x));
}
//t.Inorder();
cout << t.IsRBTree() << endl;
}