RB-tree 红黑树是一种广泛使用的平衡二叉搜索树(BInary Search Tree),也是 SGI STL 为以实现的一种搜索树,作为关联式容器(associated containers)的底部机制。它能确保任何一个节点的左右子树的高度差不会超过二者中较低子树的一倍。
RB-tree 满足以下规则:
- 每个节点不是红色就是黑色
- 根节点是黑色
- 如果节点是红色,其子节点必须为黑
- 任意节点到 NULL(数尾端)的任何路径,所含黑节点数必须相同
RB-tree 节点设计
RBTree 有红黑两种颜色的节点,并拥有左右子节点,且需要经常回溯其父节点,因此我们需要定义为每个节点定义颜色,并且定义指针指向该结点的父节点。
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; // RBTree 的许多操作必须知道父节点
base_ptr left; // 指向左节点
base_ptr right; // 指向右节点
static base_ptr minimum(base_ptr x)
{
while (x->left != 0) x = x->left; // 一直向左走就会找到最小值
return x;
}
static base_ptr maximum(base_ptr x)
{
while (x->right != 0) x = x->right; // 一直向右走就会找到最大值
return x;
}
};
template<class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field; // 节点值
}
RB-tree 的迭代器
SGI 将 RBTree 迭代器实现为两层:__rb_tree_node
继承自 __rb_tree_node_base
,__rb_tree_iterator
继承自 __rb_tree_base_iterator
。
RB-tree 迭代器属于双向迭代器,但不具备随机定位能力。其 reference 和成员访问操作与 list 十分相似,而前进和后退操作比较特殊。operator++()
调用基层迭代器的 increment()
,后退操作operator--()
则调用基层迭代器的 decrement()
。
// 基层迭代器
struct __rb_tree_base_iterator
{
typedef __rb_tree_node_base::base_ptr base_ptr;
// 提供表示双向迭代iterator_category器的函数的返回类型
typedef bidirectional_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
base_ptr node; // 用来与容器产生连接关系(make a reference)
// 以下的两个函数可实现于 operator++ 和 operator-- 内,此外再不会调用此函数
void increment()
{
if (node->right != 0) { // 如果有右子节点
node = node->right; // 就向右走
while (node->left != 0) // 然后一直往左子树走到底
node = node->left;
}
else { // 没有右子节点
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 = node->right; // 右子节点即为解答
// 以上情况发生于 node 为 header 时(亦即 node 为 end() 时)
// 注意,header 的右子节点即 mostright,指向整棵树的 max 节点
else if (node->left != 0) { // 如果有左子节点
base_ptr y = node->left; // 令 y 指向左子节点
while (y->right != 0) // 当 y 有右子节点时
y = y->right; // 一直往右子节点走到底
node = y;
}
else { // 即非根节点,也无左子节点
base_ptr y = node->parent; // 找出父节点
while (node == y->left) { // 当现节点自身为左子节点
node = y; // 一直交替往上走
y = y->parent; // 直到现节点不为左子节点
}
node = y; // 此时父节点即为解答
}
}
};
// RBTree 正规迭代器
template<class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator
{
typedef Value value_type;
typedef Ref reference;
typedef Ptr pointer;
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; }
#ifdef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() cosnt { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_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 的数据结构
RB-tree 的定义中有专属的空间配置器,每次配置一个节点大小;各种型别的定义用于维护整颗树的数据(仿函数 functor 用来表现节点的大小比较方式),以及一些 member functions 的定义。
template<class Key, class Value, class KeyOfValue, class Compare,
class Alloc = alloc>
class rb_tree {
protected:
typedef void* void_pointer;
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
typedef simple_alloc<rb_tree_node, Alloc> rb_tree_node_allocator;
typedef __rb_tree_color_type color_type;
public:
typedef Key key_type;
typedef Value value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef rb_tree_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
link_type get_node() { return rb_tree_node_allocator::allocate(); }
void put_node(link_type p) { rb_tree_node_allocator::deallocate(p); }
link_type create_node(const value_type& x) {
link_type tmp = get_node(); // 配置空间
__STL_TRY {
construct(&tmp->value_field, x); // 构造内容
}
__STL_UNWIND(put_node(tmp));
return tmp;
}
link_type clone_node(link_type x) { // 复制一个节点(值和颜色)
link_type tmp = create_node(x->value_field);
tmp->color = x->color;
tmp->left = 0;
tmp->right = 0;
return tmp;
}
void destroy_node(link_type p) {
destroy(&p->value_field); // 析构内容
put_node(p); // 释放内存
}
protected:
// RB-tree 只以三笔数据表现
size_type node_count; // 追踪记录树的大小(节点数量)
link_type header;
Compare key_compare; // 节点间的键值大小比较准则
// 以下三个函数用来取得 header 的成员
link_type& root() const { return (link_type&) headder->parent; }
link_type& leftmost() const { return (link_type&) header->left; }
link_type& righttmost() const { return (link_type&) header->right; }
// 以下六个函数用来取得节点 x 的成员
static link_type& left(link_type x)
{ return (link_type&)(x->left);}
static link_type& right(link_type x)
{ return (link_type&)(x->right);}
static link_type& parent(link_type x)
{ return (link_type&)(x->parent);}
static reference value(link_type x)
{ return x->value_field;}
static const Key& key(link_type x)
{ return KeyOfValue()(value(x));}
static color_type& color(link_type x)
{ return (color_type&)(x->color);}
// 以下六个函数用来取得节点 x 的成员
static link_type& left(base_type x)
{ return (link_type&)(x->left);}
static link_type& right(base_ptr x)
{ return (link_type&)(x->right);}
static link_type& parent(base_ptr x)
{ return (link_type&)(x->parent);}
static reference value(base_ptr x)
{ return ((link_type)x)->value_field;}
static const Key& key(base_ptr x)
{ return KeyOfValue()(value(link_type(x)));}
static color_type& color(base_ptr x)
{ return (color_type&)(link_type(x)->color);}
// 求取极大值和极小值,node class 实现了这个功能
static link_type minium(link_type x) {
return (link_type) __rb_tree_node_base::minimun(x);
}
static link_type maximum(link_type x) {
return (link_type) __rb_tree_node_base::maximum(x);
}
// 定义迭代器 iterator
public:
typedef __rb_tree_iterator<cvalue_type, reference, pointer> iterator;
private:
iterator __insert(base_ptr x, base_ptr y, const value_type& v);
link_type __copy(link_type x, link_type p);
void __erase(link_type x);
void init() {
header = get_node; // 产生一个节点空间,令 header 指向它
color(header) = __rb_tree_red; // 令 header 为红色,用力啊区分 header
// 和 root,在 iterator.operator++ 中
root() = 0;
leftmost() = header; // 令 header 的左子节点为自己
rightmost() = header; // 令 header 的右子节点为自己
}
public:
rb_tree(const Compare& comp = Compare())
: node_count(0), key_compare(comp) { init(); }
~rb_tree() {
clear();
put_node(header);
}
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>&
operator=(const rb_tree<Key, Value, KeyOfValue, Compare, Alloc>& x);
public:
Compare key_comp() const { return key_compare; }
iterator begin() { return leftmost(); } // RBTree 起始位置为最左(最小)节点
iterator end() { return header; } // RBTree 的终点为 header 所指
bool empty() const { return node_count; }
size_type size() const { return node_count; }
size_type max_size() const { return size_type(-1); }
public:
// 将 x 插入到 RBTree 中,保持节点值独一无二
pair<iterator, bool> insert_unique(const value_type& x);
// 将 x 插入到 RBTree 中,允许节点值重复
iterator insert_equal(const value_type& x);
};
RBTree 的构造和内存管理
RBTree 定义的专属空间配置器 rb_tree_node_allocator
每次配置一个节点,使用 simple_alloc<>
RBTree 有两种构造方式:(1)以现有 RBTree 复制一个新的;(2)产生一颗空树。
rb_tree<int, int, identity<int>, less<int> > itree;
上述代码分别制定了节点的键值、实值、大小比较标准等,然后调用了构造函数:
rb_tree(const Compare& comp = Compare())
: node_count(0), key_compare(comp) { init(); }
private:
void init() {
header = get_node(); // 产生一个节点空间,令 header 指向它
color(header) = __rb_tree_red; // 令 header 为红色,区分 header 和
// root
root() = 0;
leftmost() = header; // 令 header 的左子节点为自己
rightmost() = header; // 令 header 的右子节点为自己
}
RBTree 的元素操作
RBTree 提供两种插入操作:insert_unique()
和 insert_equal()
,二者两个函数有多个不同的版本。
元素插入操作 insert_equal()
insert_equal()
插入节点的键值允许在整棵树中重复。
// 返回一个 RBTree 迭代器,指向新增节点
template<class Key, class Value, class KeyOfValue, class Compare, class Alloc>
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_equal(const Value& v)
{
link_type y = header;
link_type x = root(); // 从根节点开始
while (x != 0) { // 从根节点开始,往下寻找适当的插入点
y = x;
x = key_compare(KeyOfValue()(v), key(x)) ? left(x) : right(x);
// 遇到“大”则往左,遇“小于或等于”则右
}
return __insert(x, y, v);
// x 为新值插入点,y 为插入点的父节点,v 为新值
}
元素插入操作 insert_unique()
insert_unique()
插入节点的键值不允许在整棵树中重复。返回值是个 pair
,第一个元素是 RB-tree 迭代器指向新增节点,第二元素表示插入是否成功。
pair 是将2个数据组合成一组数据,当需要这样的需求时就可以使用 pair,如 STL 中的 map 就是将 key 和 value 放在一起来保存。另一个应用是,当一个函数需要返回 2 个数据的时候,可以选择 pair。 pair 的实现是一个结构体,主要的两个成员变量是 first second 因为是使用 struct 不是 class,所以可以直接使用 pair 的成员变量。
其标准库类型–pair类型定义在#include 头文件中,定义如下:
类模板:template<class T1, class T2> struct pair
参数:T1是第一个值的数据类型,T2 是第二个值的数据类型。
功能:pair将一对值(T1 和 T2)组合成一个值,
- 这一对值可以具有不同的数据类型(T1 和 T2),
- 两个值可以分别用 pair 的两个公有函数 first 和 second 访问。
template<class Key, class Value, class KeyOfValue,
class Compare, class Alloc>
pair<typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator,
bool>
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::
insert_unique(const Value& v)
{
link_type y = header;
link_type x = root(); // 从根节点开始
bool comp = true;
while (x != 0) { // 从根节点开始,往下寻找适当的插入点
y = x;
x = key_compare(KeyOfValue()(v), key(x)) ? left(x) : right(x);
// 遇到“大”则往左,遇“小于或等于”则右
}
// 离开 while 后 y 必定指向叶节点(插入点的父节点)
iterator j = iterator(y); // 令迭代器 j 指向插入点的父节点 y
if (j == begin()) // 如果插入点的费节点为最左节点
return pair<iterator, bool>(__insert(x, y, v), true);
// 以上 x 为插入点, y 为插入点的父节点,v 为新值
else // 否则插入点的父节点不是最左节点
--j; // 调整 j
if (key_compare(key(j.node), KeyOfValue()(v)))
// 小于新值,遇到小就插入右侧
return pair<iterator, bool>(__insert(x, y, v), true);
// 否则新值一定与树中键值重复
return pair<iterator, bool>(l, false);
}
真正的插入执行程序 __insert()
template <class Key, class Value, class KeyOfValue,
class Compare, class Alloc>
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iteraotr
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::
__insert(base_ptr x_, base_ptr y_, const Value& v) {
// 参数 x_ 为新值插入点,参数 y_ 为插入点的父节点,参数 v 为新值
link_type x = (link_type) x_;
link_type y = (link_type) y_;
link_type z;
// key_compare 是键值大小比较准则,是一个 function object
if (y == header || x != 0 || key_compare(KeyOfValue()(v), key(y))){
z = create_node(v); // 产生一个新节点
left(y) = z; // 使得当 y 为 header 时,leftmost() = z
if (y == header) {
root() = z;
rightmost() = z;
}
else if (y == leftmost()) // 如果 y 为最左节点
leftmost() = z; // 维护 leftmost() 使永远指向最左节点
}
else {
z = create_node(v);
right(y) = z; // 让新节点成为插入点父节点的右子节点
if (y == rightmost())
rightmost() = z; // 维护 rightmost() 使永远指向最右节点
}
parent(z) = y; // 设置新节点的父节点
left(z) = 0; // 设置新节点的左子节点
right(z) = 0; // 设置新节点的右子节点
// 新节点的颜色将在 __rb_tree_rebalance() 设定
__rb_tree_rebalance(z, header->parent);
++node_count; // 节点数累加
return iterator(z);
}
调整 RB-tree(旋转及改变颜色)
任何插入操作完成后都要进行一次调整,使树的状态满足红黑树的要求。新插入节点的颜色都为红色,如果其父节点为黑色,这样可以直接插入而不影响路径中黑色节点的数量。但如果插入节点的父节点为红色,则破坏了红黑树的规则,即不能有连续的两个红色节点。
情况一:父节点与伯父节点的颜色都是红色,则将父节点与伯父节点改为黑色,将祖父节点改为红色,继续向上检查,直到父节点为黑色节点停止。
情况二:伯父节点为黑色或空,且祖父节点、父节点和插入节点不再一条直线上,则进行两次旋转操作:当父节点为祖父节点的左子节点且插入节点为右子节点,则对其父节点左旋,对祖父节点进行右旋;否则则旋转方向相反。
情况三:伯父节点为黑色或空,且祖父节点、父节点和插入节点在一条直线上,则对其祖父节点进行左旋或右旋操作。
// 全局函数
// 调整红黑树,改变颜色及旋转树形
// 参数一为新增节点,参数二为 root
inline void
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
x->color = __rb_tree_red; // 新节点必须为红色
// 如果插入节点的父节点为红色,则必须对其进行调整
while (x != root && x->parent->color == __rb_tree_red) {
if (x->parent == x->parent->parent->left) { // 父节点为祖父节点左子节点
__rb_tree_node_base* y = x->parent->parent->right; // y 为伯父节点
// 情况一:父节点和伯父节点都为红色
if (y && y->color == __rb_tree_red) {
x->parent->color = __rb_tree_black; // 让父节点为黑
y->color = __rb_tree_black; // 让伯父节点为黑
x->parent->parent->color = __rb_tree_red; // 让祖父节点为红
x = x->parent->parent;
}
else { // 无伯父节点或伯父节点为黑
// 情况二:新节点为父节点的右子节点
if (x == x->parent->right) {
x = x->parent;
__rb_tree_rotate_left(x, root); // 对其父节点左旋
}
// 如不满足情况二,则为情况三
x->parent->color = __rb_tree_black;
x->parent->parent->color = __rb_tree_red;
__rb_tree_rotate_right(x->parent->parent, root);
}
}
else { // 父节点为祖父节点的右子节点
__rb_tree_node_base* y = x->parent->parent->left; // y 为伯父节点
// 情况一
if (y && y->color == __rb_tree_red) {
x->parent->color = __rb_tree_black; // 让父节点为黑
y->color = __rb_tree_black; // 让伯父节点为黑
x->parent->parent->color = __rb_tree_red; // 让祖父节点为红
x = x->parent->parent; // 准备往上层检查
}
else { // 无伯父节点或伯父节点为黑
// 情况二:新节点为父节点的左子节点
if (x == x->parent->left) {
x = x->parent;
__rb_tree_rotate_right(x, root); // 对其父节点右旋
}
// 如不满足情况二,则为情况三
x->parent->color = __rb_tree_black;
x->parent->parent->color = __rb_tree_red;
__rb_tree_rotate_left(x->parent->parent, root);
}
}
} // while 结束
root->color = __rb_tree_black; // 根节点为黑色
}
旋转函数:
- 左旋函数:让旋转点 x 的右子节点 y 的左子节点连接在 x 的右子节点上,然后让 y 替代 x 的位置,让 x 连接在 y 的右子节点。
- 右旋函数:让旋转点 x 的左子节点 y 的右子节点连接在 x 的左子节点上,然后让 y 替代 x 的位置,让 x 连接在 y 的左子节点。
// 全局函数
inline void
__rb_tree_rotate_left(__rb_tree_node_base* x,
__rb_tree_node_base*& root)
{
// x 为旋转点
__rb_tree_node_base* y = x->right; // y 为旋转点的右子节点
// 连接 y 的左子节点在 x 右子节点上
x->right = y->left;
if (y->left != 0)
y->left->parent = x;
// 让 y 完全顶替 x 的地位
y->parent = x->parent;
if (x == root)
root = y;
else if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
y->left = x;
x->parent = y;
}
inline void
__rb_tree_rotate_right(__rb_tree_node_base* x,
__rb_tree_node_base*& root)
{
// x 为旋转点
__rb_tree_node_base* y = x->left; // y 为旋转点的左子节点
// 连接 y 的左子节点在 x 右子节点上
x->left = y->right;
if (y->right != 0)
y->right->parent = x;
// 让 y 完全顶替 x 的地位
y->parent = x->parent;
if (x == root)
root = y;
else if (x == x->parent->right)
x->parent->right = y;
else
x->parent->left = y;
y->right = x;
x->parent = y;
}
元素的搜寻 find
RB-tree 提供 find 函数用于查找元素:
template <class Key, class Value, class KeyOfValue,
class Compare, class Alloc>
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iteraotr
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::find(const Key& k) {
link_type y = header;
link_type x = root();
while (x != 0) {
if (!key_compare(key(x), k))
// x 键值大于 k,遇到大值就往左走
y = x, x = left(x);
else
// x 键值大于 k,遇到小值就往右走
x = right(x);
}
iterator j = iterator(y);
return (j == end() || key_compare(k, key(j.node))) ? end() : j;
}