Chapter 5: 关联式容器之 RB_tree

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

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

  1. 每个节点不是红色就是黑色;
  2. 根节点为黑色;
  3. 如果节点为红,其子节点必须为黑;
  4. 任一节点至 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) {   //伯父节点存在,且为红
                                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);
        ...
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值