Chapter 5: 关联式容器之 RB_tree

来源:互联网 发布:随机算法 知乎 编辑:程序博客网 时间:2024/06/05 17:57

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

红黑树(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;       //红色为0const __rb_tree_color_type __rb_tree_black = true;      //黑色为1struct __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()是具备如此能力的一个全局函数,代码如下:

//全局函数//重新令树形平衡(改变颜色以及旋转树形)//参数一为新增节点,参数二为 rootinine 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
原创粉丝点击