您的位置:首页 > 其它

Chapter 5: 关联式容器之 RB_tree

2017-01-15 14:23 225 查看

一:红黑树需要满足的性质

红黑树(RB-tree)不仅是一个二叉搜书树(binary search tree),还必须要满足如下的性质:

每个节点不是红色就是黑色;

根节点为黑色;

如果节点为红,其子节点必须为黑;

任一节点至 NULL (树尾端)的任何路径,所含之黑节点数必须相同;为方便起见,我们将NULL视为黑。

二:RB-tree 的节点设计

RB-tree 有红黑两色,并且拥有左右子节点。其节点设计代码如下:

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) {
while (x->left != nullptr)      //一直向左走,就会找到最小值
x = x->left;
return x;
}

static base_ptr maximum(base_ptr x) {
while (x->right != nullptr) 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 的迭代器

由于节点设计分为两层,因此相对应的 RB-tree 迭代器的设计也分为两层,
__rb_tree_node
继承
__rb_tree_node_base
,相对应的
__rb_tree_iterator
继承
__rb_tree_base_iterator
。并且 RB-tree 的迭代器属于双向迭代器。设计代码如下:

struct __rb_tree_base_iterator {

typedef __rb_tree_node_base::bas_ptr    base_ptr;
typedef bidirectional_iterator_tag      iterator_category;
typedef ptrdiff_t       difference_type;

base_ptr node;  //它用来与容器之间产生一个连接关系

//以下其实可以实现于operator++内,因为再无他处会调用此函数
void increment() {
if (node->right !=nullptr) {                    //如果有右子节点
node = node->right;             //就向右走
while (node->left != nullptr)           //然后一直往左子树走到底
node = node->left;      //即是解答
}
else {                                  //没有右子节点
base_ptr y = node->parent;      //找出父节点
while (node == y->right){       //如果node 节点本身就是其父节点的右子节点
node = y;               //就一直上溯,直到“不为右子节点”止
y = node->parent;
}
//以下考虑的是一种特殊情况:欲寻找根节点的下一节点,而恰巧根节点无右子节点
if (node->right != y)           //若此时的右子节点不等于此时的父节点
node = y;               //此时的父节点即为解答
//否则此时的 node 即为解答
}

}
//以下可用于实现operator -- 内
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 != nullptr) {       //如果有左子节点
base_ptr y = node->left; //令 y 指向左子节点
while (y->right != nullptr)    //当 y 有右子节点时
y = y->right;    //一直往右节点走到底
node = y;               //最后即为答案
}
else {                          //即非根节点,也无左子节点
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;
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; }
#ifndef __SGI_STL_NO_ARROW_OPERATION
pointer operator->() const { return &(*operator()); }
#endif /* __SGI_STL_NO_ARROW_OPERATION*/

self& operator++() { increment(); return *this; }
self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}

self& operator--() { decrement(); return *this; }
self operator--(int) {
self tmp = *this;
--*this;
return tmp;
}
};


四:RB-tree 的构造与内存管理

1:RB-tree 空间配置器

RB-tree 定义了专属空间配置器
rb_tree_node_allocator
,每次可恰恰配置一个节点,代码如下:

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
class rb_tree {
protected:
typedef __rb_tree_node<Value>   rb_tree_node;
typedef simple_alloc<rb_tree_node, Alloc>       rb_tree_node_allocator;
...
};


2:RB-tree 的构造方式

RB-tree 的构造方式有两种,一种是以现有的 RB-tree 复制一个新的 RB-tree;另一种是产生一棵空空如也的树,代码构造如下所示:

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc = alloc>
class rb_tree {
public:
rb_tree(const Compare& comp = Compare()) : node_count(0), key_compare(comp) { init(); }


其中
init()
函数的代码如下:

private:
void init() {
header = get_node();
color(header) = __rb_tree_red;  //令 header 为红色,用来区分 header
// 和 root,在 iterator.operator--之中
root() = nullptr;
leftmost() = header;            //令 header 的左子节点为自己
rightmost() = header;           //令 header 的右子节点为自己
}


其中的 header 是 SGI STL 为树的根节点特别设计的一个父节点,该 header 的父节点指向根节点,左子节点指向最小节点,右子节点指向最大节点;

五:RB-tree 的元素操作

1:
insert_unique()
insert_equal()
函数

insert_unique()
函数表示被插入节点的键值(key)在整棵树种必须独一无二,因此,如果树中已存在相同的键值,插入操作就不会真正进行;
insert_equal()
表示被插入节点的键值在整棵树中可以重复,因此,无论如何插入都会成功。

insert_equal()
函数代码如下:

//插入新值,节点键值允许重复
//注意,返回值是一个 RB-tree 迭代器,指向新增节点
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
auto rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_equal(const Value& v)
-> iterator
{
link_type y = header;
link_type x = root();   //从根节点开始
while (x != nullptr) {  //从根节点开始,往下寻找适当的插入点
y = x;
x= key_compare(KeyOfValue()(v), key(x)) ? left(x) : right(x);
}

return __insert(x, y, v);
//以上, x 为新值插入点,y 为插入点之父节点,v 为新值
}


insert_unqiue()
函数代码如下:

//插入新值:节点键值不允许重复,若重复则插入无效
//注意,返回值是一个 pair,第一个元素是 RB-tree 迭代器,指向新增结点
//第二元素表示插入成功与否
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
auto rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_unique(const Value& v)
-> pair<iterator, bool>
{
link_type y = header;
link_type x = root();   //从根节点开始
bool comp = true;
while (x != nullptr) {
y = x;
comp = key_compare(KeyOfValue()(v), key(x));  //v 键值小于目前节点之键值?
x = comp ? left(x) : right(x);  //遇 “大” 则往左,遇“小于等于”则往右
}
// 离开 while 循环后,y 所指即插入点之父节点(此时的它必为叶节点)

iteraor j = iterator(y); //令迭代器 j 指向插入点之父节点 y
if (comp)       //表示遇“大”,将插入节点 y 左侧
if (j == begin())       //如果插入点之父节点为最左节点
return pair<iterator, bool>(__insert(x, y, v), true);
else    //否则(插入点之父节点不为最左节点)
--j;    //调整 j,回头准备测试
if (key_compare(key(j.node), KeyOfValue()(v)))
//新键值不与既有节点之键值重复,于是执行以下插入操作
return pair<iterator, bool>(__insert(x, y, v), true);
//以上,x 为新值插入点,y 为插入点之父节点,v 为新值

//进行至此,表示新值一定与树中键值重复,那么就不该插入新值
return pair<iterator, bool>(j, false);
}


2:真正的执行插入程序
__insert()

在上面的两个函数中,都利用到了
__insert()
这个函数,该函数的代码如下:

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
auto rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::__insert(base_ptr x_, base_ptr y_, const Value& v)
-> iterator
//参数 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 != nullptr || 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;           //令新节点成为插入点之父节点 y 的右子节点
if (y == rightmost())           //如果 y 为最右节点
rightmost() = z;       //维护 rightmost(),使它永远指向最右节点
}

//调整新增节点之父节点和左右子节点
parent(z) = y;
left(z) = nullptr;
right(z) = nullptr;

//设置新增节点的颜色以及调整树的平衡
__rb_tree_reblaance(z, header->parent);    //参数一为新增节点,参数二为 root
++node_count;   //节点数累加
return iterator(z);     //返回一个迭代器,指向新增节点
}


3:调整 RB-tree(旋转及改变颜色)

任何插入操作,于节点插入完毕后,都要做一次调整操作,将树的状态调整到符合 RB-tree 的要求。
__rb_tree_rebalance()
是具备如此能力的一个全局函数,代码如下:

//全局函数
//重新令树形平衡(改变颜色以及旋转树形)
//参数一为新增节点,参数二为 root
inine 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) {   //伯父节点存在,且为红

10c7d
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_redl
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;  //根节点永远为黑
}


4:节点左旋转和右旋转函数

在 RB-tree 的调整中,除了改变节点颜色之外,还需要对节点进行左旋转以及右旋转,代码如下:

//全局函数
//节点左旋转
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 为旋转点的右子节点
x->right = y->left;
if (y->left != 0)
y->left->parent = x;
y->parent = x->parent;

//令 y 完全顶替 x 的地位
if (x == root)          //x 为根节点
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;
x->left = y->right;
if (y->right != nullptr)
y->right->parent = 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;
}


5:元素搜寻操作

RB-tree 是一个二叉搜索树,其提供的元素搜寻函数
find()
代码如下:

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>
auto rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::find(const Key& k) -> iterator
{
link_type y = header;
link_type x = root();

while (x != nullptr) {
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;
}


六:RB-tree 的数据结构

RB-tree 比较完整的数据结构代码如下:

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(&(p->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 = nullptr;
tmp->right = nullptr;
tmp->right = nullptr;
}

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;   //节点间的键值大小比较准则,应该会是一个 function object

//以下三个函数用来方便取得 header 的成员
link_type& root() const { return (link_type&) header->parent; }
link_type& leftmost() const { return (link_type&) header->left; }
link_type& rightmost() 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_ptr 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 minimum(link_type x) {
return (link_type)__rb_tree_node_base::minimum(x);
}
static link_type maximum(link_type x) {
return (link_type)__rb_tree_node_base::maximum(x);
}

public:
typedef __rb_tree_iterator<value_type, reference, pointer> iterator;
typedef __rb_tree_iterator<value_type,const reference, const pointer> const_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();
color(header) = __rb_tree_red;  //令 header 为红色,用来区分 header
// 和 root,在 iterator.operator--之中
root() = nullptr;
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& operator=(const rb_tree& x);

public:
Compare key_comp() const { return key_compare; }
iterator begin() { return leftmost(); } //RB 树的起头为最左(最小)节点处
iterator end() { return header; }       //RB 树的终点为 header 所指处
bool empty() const { return node_count==0; }
size_type size() const { return node_count; }
size_type max_size() const { return size_type(-1); }

public:
//将 x 插入到 RB-tree 中(保持节点独一无二)
pair<iterator, bool> insert_unqiue(const value_type& x);
//将 x 插入到 RB-tree 中(允许节点值重复)
iterator insert_equal(const value_type& x);

//寻找 RB 树中是否有键值为 k 的节点
iterator find(const Key& k);
...
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: