、//本文中的图片来自于http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html
什么是红黑树?
红黑树首先是一棵平衡二叉搜索树,与AVL树不同的是,红黑树的每个节点都有颜色,要么黑,要么红。概括就是以下几点:
1,红黑树是一个近似平衡的二叉搜索树
2,红黑树每个节点都有颜色
3,红黑树根节点的颜色一定是黑色。
4,红黑树的每棵子树中,黑色节点的数量一定相等。
5,红黑树中不能有连续的红色节点出现。
红黑树的基本规则就这些,相信在看红黑树之前对AVL树有一定的了解。下面我们来看看红黑树与AVL树的区别:
1,红黑树的平衡性是近似平衡,左子树与右子树的高度差不超过两倍,而AVL树左子树与右子树之间的高度差为1。(这就会导致在删除和插入操作后,红黑树调整维持自身特性时采用的旋转操作比AVL次数小许多,通常在常数次就能完成)。
2,AVL树的平衡需要依靠其每个节点的平衡因子去维持,而红黑树的平衡依靠其节点的颜色维持。
下面来看红黑树的构建:
这里使用c++模板实现,
首先是其数据结构
enum Colour
{
RED,
BLACK,
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
K _key;
V _value;
Colour _col;
RBTreeNode(const K& key, const V& value)
:_left(NULL)
, _right(NULL)
, _parent(NULL)
, _key(key)
, _value(value)
, _col(RED)
{}
};
红黑树通过其根节点来找到其他节点,所以其构造函数只需要将根节点初始化即可。
然后就是其插入操作,在上面说过,插入时会引起红黑树结构的变化,就需要通过旋转来调整,使它重新平衡。
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->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
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;
}
// 检查规则,调平衡
//1,p为黑
//2,p为红,u为红
//3,p为红,u不存在或者为黑
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//uncle存在
if (uncle&&uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上调节
cur = grandfather;
parent = cur->_parent;
}
else //uncle不存在或者uncle为黑色
{
if (cur == parent->_right)
{
RotateL(parent);
swap(parent, cur);
}
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle&&uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else//uncle不存在或者为黑
{
if (cur == parent->_left)
{
RotateR(parent);
swap(parent, cur);
}
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
}
}
_root->_col = BLACK;
return true;
}
红黑树的难点就在于其平衡性的调整,下面我们通过几幅图片来说明红黑树的旋转
通过图片可以看到,我们将s节点的左孩子赋给了h节点的右孩子,然后将s节点的左孩子指向h节点,h节点的父亲指向s节点
//parent节点就是图中的h节点,subR=s,subrl=s->left;
void RotateL(Node* parent)
{
assert(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 (ppNode == NULL)
{
_root = subR;
printf("ppNode==NULL\n");
return;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subR;
subR->_parent = ppNode;
}
else
{
ppNode->_right = subR;
subR->_parent = ppNode;
}
}
_root->_parent = NULL;
}
接下来看右旋
通过图片我们可以看到,右旋是左旋的逆向,算法思想和操作基本一致,接下来看代码
void RotateR(Node* parent)
{
assert(parent);
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == NULL)
{
_root= subL;
//subL->_parent = NULL;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
subL->_parent = ppNode;
}
else
{
ppNode->_right = subL;
subL->_parent = ppNode;
}
}
_root->_parent = NULL;
}
那么什么情况下才会引起红黑树的旋转呢?
我们知道,红黑树由文章开头讲的几条规则来构建,所以违反了任意一天规则都会引起红黑树的变化。
以插入操作为例:我们每次插入的新节点都是红色节点,所以最多在插入第四个节点的时候就会违背不能有连续的红色节点这条原则,此时就会引起旋转来调整红黑树的平衡性。
而我们如何保证我们的插入算法是正确的,那么我们就写一个判断红黑树是否平衡的算法来判断。
代码如下
bool IsBalance()
{
if (_root && _root->_col == RED)
return false;
size_t count = 0;
Node* cur = _root;
//统计出其最左子树的黑节点数量
while (cur)
{
if (cur->_col == BLACK)
{
count++;
}
cur = cur->_left;
}
size_t k = 0;
return _IsBalance(_root,count,k);
}
bool _IsBalance(Node* root,const size_t count,size_t k)
{
if (root == NULL)
{
if (count != k)
{
cout << "黑节点数量不相等" << endl;
return false;
}
return true;
}
//判断是否有连续的红色节点。
if (root->_col == RED && root->_parent->_col == RED)
{
return false;
}
if (root->_col == BLACK)
k++;
return _IsBalance(root->_left,count,k)&&
_IsBalance(root->_right,count,k);
}
下面来分析红黑树的效率:
在最坏的情况下(即红黑相间的树的高度是全黑路径树的两倍),红黑树的高度不会超过2lgN,
红黑树的应用:
1,linux系统底层实现,
2,C++的STL库中map,multimap,set,multiset的实现
等等