Chapter 5: 关联式容器之 RB_tree
来源:互联网 发布:随机算法 知乎 编辑:程序博客网 时间:2024/06/05 17:57
一:红黑树需要满足的性质
红黑树(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; //红色为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); ...};
- Chapter 5: 关联式容器之 RB_tree
- Chapter 5: 关联式容器之 hashtable
- Chapter 5: 关联式容器之 set 和 multiset
- Chapter 5:关联式容器之 map 和 multimap
- C++ Primer Chapter 10 关联容器之map
- C++ Primer Chapter 10 关联容器之set & mutimap
- Chapter 14.关联容器set
- Chapter 15.关联容器map
- Chapter 16.关联容器multiset
- Chapter 17.关联容器multimap
- 简述容器之rb_tree及其应用--(boolan)
- C++ Primer Chapter 10 关联容器之容器的综合应用:文本查询程序
- Chapter 4: 序列式容器之 vector
- Chapter 4: 序列式容器之 list
- Chapter 4:序列式容器之 deque
- STL之关联式容器
- C++之关联式容器
- chapter 10 关联容器(待续)
- Android之---仿斗鱼直播弹屏效果的实现
- 问题三十五: 怎么用ray tracing画二次曲面(quadratic surfaces)(1)——椭球面
- Android5.0中特性的介绍
- numpy中linspace用法
- HTML页面中点击按钮关闭页面几种方式与取消
- Chapter 5: 关联式容器之 RB_tree
- POJ 2635The Embarrassed Cryptographer(数论-素数筛)
- mantis搭建在win7
- js平滑滚动到顶部,底部,指定地方
- iTop4412之DS18B20驱动开发
- 2017年全球IT支出将增长2.7%
- 2016回顾总结&2017年计划展望
- eclipse项目目录结构和路径
- Java代码实现获取XML字符串中的属性