1.红黑树的简介
红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位_color来表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子简单
路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。红黑树没有达到AVL树的高度平衡,换句话说,它的高度,并没有AVL树那么高的要求,但他的应用却更加的广泛,实践中是相当高效的,他可以在O(log2 n)的时间内做查找、插入、删除操作。在C++ STL中,set、multiset、map、multimap等都应用到的红黑树的变体。
2.红黑树的性质
(1)每个节点,不是红色就是黑色的
(2) 根节点是黑色的
(3) 如果一个节点是红色的,则它的两个子节点是黑色的
(4) 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
(5)每个叶子节点都是黑色的(这里的叶子节点是指的NIL节点(空节点))
3.节点的定义
enum Color
{
RED,
BLACK
};
//三叉链
template<typename K,typename V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
int _color;
RBTreeNode(const K& key, const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _value(value)
, _color(RED)
{}
};
有了AVL树的经验,红黑树旋转的时候并不是调节平衡因子,而是进行颜色。
要知道新插入的结点一定是红色,否则不能满足性质4.
插入操作:
1.树为空直接创建根节点,根节点设置为黑色
2.树不为空,就要分情况讨论了。
1>第一种情况
cur为红,p为红,g为黑,u存在且为红
则将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
2>第二种情况
cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红(如果叔叔存在,cur为调整上来的节点,若叔叔不存在,cur为新插入的结点)
3>第三种情况
cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2,但要注意的是第一次旋转之后,cur与parent的位置
下面给出插入操作的代码:同样也是先找到插入位置(二叉搜索树的插入)
bool Insert(const K& key, const V& value)
{
if (_root == NULL)
{
_root = new Node(key, value);
_root->_color = BLACK;
return true;
}
else
{
Node* parent = NULL;
Node* cur = _root;
//找插入位置
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(key, value);
if (parent->_key>key)
{
parent->_left = cur;
cur->_parent = parent;
}
else if (parent->_key < key)
{
parent->_right = cur;
cur->_parent = parent;
}
while (parent&&parent->_color == RED)
{
Node* grandfather = parent->_parent;
if (parent==grandfather->_left)
{
Node* uncle = grandfather->_right;
//叔叔存在且为红
if (uncle&&uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//叔叔不存在或者存在为黑色
{
if (cur == parent->_right)
{//2次旋转
RotateL(parent);
swap(cur, parent);
}
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
else//parent==grandfather->_right
{
Node* uncle = grandfather->_left;
//叔叔存在且为红
if (uncle&&uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandfather->_color = RED;
cur = grandfather;
parent = cur->_parent;
}
else//叔叔不存在或者存在为黑色
{
if (cur == parent->_left)
{//双旋
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
}
}
}
}
_root->_color = BLACK;
return true;
}
左旋右旋代码:
void RotateL(Node* parent)//左旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppNode = parent->_parent;//subL->_parent = parent->_parent;
parent->_parent = subR;
if (ppNode == NULL)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parent == ppNode->_left)
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;
subL->_right = parent;
Node* ppNode = parent->_parent;
parent->_parent = subL;
if (ppNode == NULL)
{
_root = subL;
subL->_parent = NULL;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
}
插入操作结束,我们可以看出没最主要的是其uncle结点的作用。
我们最后来判断一下是否平衡
bool IsBalance()
{
if (_root == NULL)
return true;
if (_root&&_root->_color == RED)
return false;
int num = 0;
int blacknum = 0;
//求最左分支的黑结点的个数
Node* left = _root;
while (left)
{
if (left->_color == BLACK)
{
++blacknum;
}
left = left->_left;
}
return _IsBalance(_root, blacknum, num);
}
bool _IsBalance(Node* root, const int blacknum, int num)
{
if (root == NULL)
{
return true;
}
if (root->_color == RED&&root->_parent->_color == RED)
{
cout << "有两个连续的红结点" << endl;
return false;
}
if (root->_color == BLACK)
{
++num;
}
if (root->_left == NULL && root->_right == NULL && num!=blacknum )
{
return false;
}
return _IsBalance(root->_left, blacknum, num)
&& _IsBalance(root->_right, blacknum, num);
}