红黑树:
红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示结点的颜色,红色或黑色(Red或Black)
通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,从而达到近似平衡。
红黑树必须满足的性质:
1,每个节点不是红色就是黑色。
2,根节点必须为黑色。
3,如果一个节点是红色,则它的两个子节点是黑色
(即不能有两个连续的红色节点)。
4,对每个节点,从该节点到其所有后代的叶子节点的简单路径上,均包含相同数目的黑色节点。
5,每个叶子节点都是黑色(这里的叶子节点指NIL结点(空结点))(如果是空结点默认为黑色)。
我们可以先来思考一个简单的问题:
为什么红黑树加上了上面的颜色约束性质,它可以保证最长的路径不超过最短路径的两倍?
我们想一下最短路径应该是全黑,最长路径应该是红黑红黑相间。这么看来,它就可以保证最长路径不超过最短路径的两倍。
学完AVL树,接触红黑树时,它们是非常相似的,都会插入删除之后调节平衡,AVL树通过平衡因子调节平衡,红黑树通过颜色约束平衡,我们把AVL树称为高度平衡的二叉搜索树,而红黑树是近似平衡的二叉搜索树
所以在插入,删除上,AVL树旋转的次数比红黑树多,所以红黑树的插入,删除效率更高。
在应用中,红黑树应用更为广泛,在STL中,set,map,multiset,multimap的底层实现都是通过红黑树来完成的
红黑树的节点结构:
因为我们用颜色约束平衡,所以我们创建一个枚举类型来保存结点的颜色
代码实现
enum Color
{
RED,
BLACK,
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
Color _col;
RBTreeNode(const K& key, const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _value(value)
, _col(RED)
{}
};
插入:
我们将红黑树的插入分为五种情况:
第一种:
第二种:
情况二具体情况:1,u不存在
如上图:如果u不存在,那么,a,b,c,d,e也一定都不存在
u存在为黑:
如情况二的图:如果u存在为黑,那么,d,e不一定存在,是下图变上去的:
第三种:
第四种:第四种情况与第二种情况是接近的,不同的是:p是g的右孩子,cur是p的右孩子。我们要进行的是左单旋。
第五种:第五种情况与第三种情况是接近的,不同的是:p是g的右孩子,cur是p的左孩子,要先进行右单旋,然后变为第四种情况。
最后两种就不用图演示了,相信看完前三种情况,最后两种也一定可以清除。
代码实现:
bool Insert(const K& key,const V& value)
{
if (_root == NULL)
{
_root = new Node(key,value);
_root->_col = BLACK;
return true;
}
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->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent&&parent->_col == RED)
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
Node* uncle = grandparent->_right;
if (uncle&&uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col