目录
一、概念
1.一棵树是红黑树的前提是这棵树是二叉搜索树
2.红黑树的结点不是红色就是黑色
3.红黑树中最长路径不超过最短路径的两倍
红黑树底层封装了map和set,这足以说明红黑树的优劣,而红黑树超高的效率的核心:
1.红黑树中根结点是黑色
2.红黑树中不存在连续的红色结点
3.红黑树的每一条路径所含有的黑色结点数量相同
满足这三点,即可满足最长路径不超过最短路径的两倍。
对上述要点作以下说明:
1.红黑树中最长路径不超过最短路径的两倍,假设最短路径为h,那么其他路径的长度就是[h,2h]。极端情况下,最短路径都是黑色结点,不存在红色结点,最长路径是一黑一红的黑红相间,但是,一棵红黑树不一定存在最长路径和最短路径,也有可能是平衡的。
2.红黑树中不存在连续的红色结点。一个红色结点的孩子必然是黑色,一个黑色结点的孩子可以是红色也可以是黑色。
二、RBTreeNode
enum Color
{
RED,
BLACK
};
template <class K,class V>
struct RBTreeNode
{
typedef RBTreeNode<K, V> Node;
Node* _left;
Node* _right;
Node* _parent;
enum Color _col;
pair<K, V> _kv;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
,col(RED)
{}
};
三、插入
在写出代码之前,先对插入的各类情况分析。
插入新结点的颜色必须是红色,如果插入新节点的颜色是黑色,则大概率会违反“红黑树的每一条路径所含有的黑色结点数量相同”。
在插入结点是红色结点后,插入结点的parent结点如果是黑色,则不需要调整,当前状态一定符合红黑树的规则。
如果插入结点的parent结点是红色,则需要分情况处理,这部分也是红黑树最为关键的部分。且插入结点的grandfather结点一定是黑色(在插入结点之前的树一定是红黑树,说明不存在连续的红色结点)。
插入结点cur ——红色
插入结点的parent —— 红色
插入结点的grandfather —— 黑色
插入结点的uncle ——???
根据uncle的不同而做不同的插入处理
情况一:uncle存在且为红色
处理方式:
parent 和 uncle 由 RED - 》 BLACK
grandfather 由 BLACK - 》 RED
如果grandfather是根结点,则还原为黑色
如果grandfather不是根结点,则让cur == grandfather,继续向上处理。
在情况一中,parent、uncle、cur是left还是right都不影响处理的结果,处理的方式都是一样的
情况二:uncle不存在或者uncle存在且为黑
如果uncle不存在,则cur一定为新增结点。
处理方式:
如果p是g的左孩子,cur是p的左孩子,则右单旋
如果p是g的右孩子,cur是p的右孩子,则左单旋
然后变色:g由黑变红,p由红变黑
如果p是g的左孩子,cur是p的右孩子,则左右双旋,然后变色:g由黑变红,cur由红变黑
如果p是g的右孩子,cur是p的左孩子,则右左双旋,然后变色
如果uncle存在且为黑色结点,则cur一定不是新增结点,而是由原来的黑色结点经过情况一的处理变出来的红色结点。此时的处理情况也是旋转+变色。
和情况一不同的是,情况二经过旋转+变色,无论是p变为黑色结点还是cur变为黑色结点,都不需要再向上处理。
基于以上分析,红黑树的插入代码实现如下:
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 (kv.first > parent->_kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//红黑树的处理
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
//情况一:uncle存在且为红
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
//情况二:uncle不存在或者uncle存在且为黑
if (cur == parent->_left)
{
//右单旋+变色
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
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
{
//情况二
if (cur == parent->_right)
{
//左单旋+变色
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
//右左单旋+变色
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
四、红黑树的验证
验证红黑树即验证下面这三点:
红黑树中根结点是黑色
红黑树中不存在连续的红色结点
红黑树的每一条路径所含有的黑色结点数量相同
//balcknum表示从根结点到当前结点这条路径的黑色结点个数
bool Check(Node* cur,int blacknum,int RefBlackNum)
{
if (cur == nullptr)
{
if (blacknum != RefBlackNum)
{
cout << "每条路径黑色结点不相同" << endl;
return false;
}
cout <<"这条路径有黑色结点个数" << blacknum << endl;
return true;
}
if (cur->_col == RED && cur->_parent->_col == RED)
{
cout << "存在连续的红色结点" << endl;
return false;
}
if (cur->_col == BLACK)
blacknum++;
return Check(cur->_left,blacknum,RefBlackNum) && Check(cur->_right,blacknum,RefBlackNum);
}
bool IsRBTree()
{
//验证根结点是否是黑色
if (_root && _root->_col == RED)
return false;
//求出一条路径上的黑色结点作参考,这里求出最左路径上的黑色结点
int RefBlackNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++RefBlackNum;
cur = cur->_left;
}
return Check(_root,0,RefBlackNum);
}
用以下代码测试实现的红黑树:
#include "RBTree.h" void testRBTree1() { RBTree<int, int> t; //int arr[] = { 4,2,16, 3, 7, 11, 9, 26, 18, 14, 15 ,2 }; int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,14 }; for (auto e : arr) { t.Insert(make_pair(e, e)); } t.InOrder(); cout << t.IsRBTree() << endl; } int main() { testRBTree1(); return 0; }
运行结果:
完整代码 红黑树
模拟STL源码,用KV模型的红黑树封装map和setgitee代码链接