文章目录
1. RB-tree 性质
RB-tree(红黑树)是一种颇具历史并被广泛运用的平衡二叉树。相比于另一种平衡二叉树AVL-tree,其对平衡性规则的要求要宽松一些,且插入删除操作更便于控制,但是搜寻平均效率几乎相等。所谓RB-tree,不仅是一个二叉搜索树,而且必须满足以下规则:
- 1. 每个节点不是红色就是黑色(图中深色底纹代表黑色,浅色底纹代表红色);
- 2. 根节点为黑色;
- 3. 如果节点为红色,其子节点必须为黑色;
- 4. 任一节点至NULL(树尾端)的任何路径,所含之黑色节点数必须相同;
总结就是:
- 红黑树的性质是每条路径的黑色节点数目相同;
- 红黑树保证最长路径不超过最短路径的二倍,因而近似平衡;
(对于红黑树中,最长路径是严格红黑相间的路径,因为红色和黑色只能相间排列,且以红色结尾,所以红色和黑色数量相等,又最短路径是全黑节点路径,且每个路径黑色数量相等,所以有此结论)
2. RB-tree 插入
根据规则4,新增节点必须为红色;根据规则3,新增节点之父节点必须为黑色。当新节点根据二叉搜索树的规则到达其插入点,却未能符合上述条件时,就必须调整颜色并旋转树形。见下图:
对于红黑树节点的插入,也包括AVL树中的左旋、右旋、左右旋、右左旋调整情形,但是旋转之后还要考虑颜色的排布符合规则要求。具体步骤如下:
- 根节点为NULL,直接插入新节点并将其颜色置为黑色;
- 根节点不为NULL,找到要插入新节点的位置;
- 插入新节点,插入的新节点默认颜色为红色;
- 判断新插入节点对全树颜色的影响,更新调整颜色;
具体的插入过程推荐点击如下的演示器链接自己试验一下!!!
共分为3大类,9种小的情况(更改颜色1,单旋4,双旋4排布),红黑树动画演示器
( 一 ) (调整颜色) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 存在且为红色
将 p a r e n t parent parent 和 u n c l e uncle uncle 节点改为黑色, p P a r e n t pParent pParent 改为红色,再将 p P a r e n t = c u r pParent=cur pParent=cur,继续向上调整(情形1)
( 二 ) (单旋) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 不存在或为黑色
2.1 p a r e n t parent parent 为 p P a r e n t pParent pParent 的左孩子, c u r cur cur 为 p a r e n t parent parent 的左孩子,则进行右单旋:
2.1.1 u n c l e uncle uncle 不存在(右单旋)(情形2)
2.1.2 u n c l e uncle uncle 存在且为黑色(右单旋)(情形3)
2.2 p a r e n t parent parent 为 p P a r e n t pParent pParent 的右孩子, c u r cur cur 为 p a r e n t parent parent 的右孩子,则进行左单旋:
2.2.1 u n c l e uncle uncle 不存在(左单旋)(情形4)
2.2.2 u n c l e uncle uncle 存在且为黑色(左单旋)(情形5)
( 三 ) (双旋) c u r cur cur 为红, p a r e n t parent parent 为红, p P a r e n t pParent pParent 为黑, u n c l e uncle uncle 不存在或为黑色
3.1 p a r e n t parent parent 为 p P a r e n t pParent pParent 的左孩子, c u r cur cur 为 p a r e n t parent parent 的右孩子,则对 p a r e n t parent parent 进行左单旋, 转换为2.2:
3.1.1 u n c l e uncle uncle 不存在(左右旋)(情形6)
3.1.2 u n c l e uncle uncle 存在且为黑色(左右旋)(情形7)
3.2 p a r e n t parent parent 为 p P a r e n t pParent pParent 的右孩子, c u r cur cur 为 p a r e n t parent parent 的左孩子,则对 p a r e n t parent parent 进行右单旋, 转换为2.1:
3.2.1 u n c l e uncle uncle 不存在(右左旋)(情形8)
3.2.2 u n c l e uncle uncle 存在且为黑色(右左旋)(情形9)
3. STL非公开rb_tree容器
|
|
具体源码可查看 SGI STL的rb_tree浅析
3.1 rb_tree 数据结构
rb_tree容器提供遍历操作及iterators。按照正常规则(++iter
,实际是二叉树的先序遍历),便能获得排序状态(sorted)。以以上红黑树为例,iter进行自加遍历之后得到的序列为
[
5
,
6
,
7
,
8
,
10
,
11
,
12
,
13
,
15
]
[5, 6, 7, 8, 10, 11, 12, 13, 15]
[5,6,7,8,10,11,12,13,15]
在使用rb_tree时不应使用其iterator改变元素值,尽管在实际编程实现上STL并没有阻止变值操作。主要原因是rb_tree作为set和map容器的底层容器,其中map<key, value>是允许对元素(即__rb_tree_node中的value )中的data进行改变,而对key(排序依据)是不能够改变的。
rb_tree提供两种insertion操作:insert_unique()
和insert_equal()
。
- unique表示节点的key一定在整个红黑树中独一无二,否则插入失败(并不会造成程序问题,count()结果为二元制0或1),这种insertion对应set/map;
- equal表示节点的key可重复,这种insertion对应multiset/multimap;
//GNU 2.9版本, GNU 4.9的实现是为了参照handle\body规范,有些得不偿失?
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
//Key:键的类型,Value:值的类型,其中key和data合成value
//KeyOfValue:如何取出key,如gnu C中的identity<int>,是一种仿函数
//Compare:key的大小比较准则,是一个函数对象function object
class rb_tree {
protected:
typedef __rb_tree_node<Value> rb_tree_node;
...
public:
typedef rb_tree_node* link_type;
...
protected:
//rb_tree以三个成员来表示自己
size_type node_count; //rb_tree的大小,也即节点数量
link_type header; //连接root的dummy节点,如上图所示
Compare key_compare //key的大小比较准则,是一个函数function object,如less<T>;或仿函数functor
//类数据就这个三个。
//4 + 4 + 1 = 9 --->12 4字节对齐
//仿函数为空,默认都是1字节。
public:
iterator begin() { return leftmost(); }//RB树的起头为最左节点(指向数值最小的节点),类里面含有一个起始迭代器和结束结束迭代器,用来管理树
iterator end() { return header; }//RB树的终点,最右边节点(指向数值最大的节点),头结点使用,使其实现左闭右开的用法。
//将x插入到RB-tree中(保持节点key值独一无二),返回一个pair对象,里面存储对应迭代器和成功与否。
pair<iterator,bool> insert_unique(const value_type& x);
//key值可以重复
//将x插入到RB-tree中(允许节点值重复),返回插入的新节点的迭代器。
iterator insert_equal(const value_type& x);
...
};
3.2 __rb_node 设计实现
节点设计分为两层,有一底层base实现,__rb_tree_node
public继承该base。__rb_tree_node
的设计源码如下:
//SGI STL
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false; //红色为0
const __rb_tree_color_type __rb_tree_black = true; //黑色为1
struct __rb_tree_node_base {
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color; //节点颜色,非红即黑
base_ptr parent; //RB树的许多操作需要知道父节点,
//这里可以想到树中两节点最低公共祖先问题中,节点有父节点指针情形,退化到两个链表的第一个交点问题
base_ptr left; //指向左孩子
base_ptr right; //指向右孩子
static base_ptr minimum(base_ptr x) { //一直向左走,就会找到最小值,BST的性质
while(x->left != 0) x = x->left;
return x;
}
...
//maximum实现略
};
template<class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field; //节点值
//一个实现见图2 __rb_tree_node构成
};
3.3 __rb_tree_iterator 设计实现
rb_tree的迭代器设计也设计为两层,base_iterator
和iterator
。RB-tree迭代器属于双向迭代器,但不具备随即定位能力,其提领操作与list十分近似,较为特殊的是其前进和后退操作。迭代器的operator++()
调用了基层迭代器的increment()
,后退操作调用了decrement()
。前进或后退的举止完全依据二叉搜索树的节点排列法则,加上实现的技巧。
//rb树的迭代器
//注意迭代器要实现的5个特性:value_type, iterator_category, difference_type, reference, pointer
struct __rb_tree_base_iterator
{
typedef __rb_tree_node_base::base_ptr base_ptr;
typedef bidirectional_iterator_tag iterator_category; //(4)
typedef ptrdiff_t difference_type; //(5)
base_ptr node;
//指向下一个节点(++操作),找到比node大的第一个节点
void increment()
{
if(node->right != 0){ //若当前节点的右树不空,则其直接前驱就在其右树部分
node = node->right; //在node的右树中找出最左的节点就是node的直接前驱
while(node->left != 0){
node = node->left;
}
}else{ //node的右树为空,则进行上溯查找node的父节点,直到找到node的直接前驱
base_ptr y = node->parent;
while(node == y->right){
node = y;
y = y->parent;
}
if(node->right != y){
node = y;
}
}
}
//指向上一个节点(--操作),找到比node小的第一个节点
void decrement()
{
if(node->color == __rb_tree_red && node->parent->parent == node){ //node为header
node = node->right;
}else if(node->left != 0){ //若node的左树不为空,就在左树中找到最右侧的节点
base_ptr y = node->left;
while(y->right != 0){
y = y->right;
}
node = y;
}else{ //node的左树为空,则上溯,查找node的父节点,找到node的直接前驱
base_ptr y = node->parent;
while(node == y->left){
node = y;
y = y->parent;
}
node = y;
}
}
};
template<class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
typedef Value value_type; //(1)
typedef Ref reference; //(2)
typedef Ptr pointer; //(3)
typedef __rb_tree_iterator<Value, Value& , Value*> iterator;
typedef __rb_tree_iterator<Value, const Value&, const Value*> const_iterator;
typedef __rb_tree_iterator<Value, Ref, Ptr> self;
typedef __rb_tree_node<Value>* link_type;
__rb_tree_iterator()
{
}
__rb_tree_iterator(link_type x)
{
node = x;
}
__rb_tree_iterator(const iterator& it)
{
node = it.node;
}
reference operator*()const
{
return link_type(node)->value_field;
}
pointer operator->()const
{
return &(operator*());
}
self& operator++()
{
increment();
return *this;
}
self operator++(int)
{
self tmp = *this;
increment();
return tmp;
}
self& operator--()
{
decrement();
return *this;
}
self operator--(int)
{
self tmp = *this;
decrement();
return tmp;
}
};
rb_tree
的无论是节点还是迭代器都是以struct
定义的原因是: struct的所有成员都是public,所以其所有成员都可被外界自由取用。
下图是迭代器和节点之间的对应关系: