「STL详解」RB-tree 红黑树
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; // 黑色为 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; // 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_OPERATORpointer 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 和// rootroot() = 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 指向插入点的父节点 yif (j == begin()) // 如果插入点的费节点为最左节点return pair<iterator, bool>(__insert(x, y, v), true);// 以上 x 为插入点, y 为插入点的父节点,v 为新值else // 否则插入点的父节点不是最左节点--j; // 调整 jif (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 objectif (y == header || x != 0 || key_compare(KeyOfValue()(v), key(y))){z = create_node(v); // 产生一个新节点left(y) = z; // 使得当 y 为 header 时,leftmost() = zif (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;elsex->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;elsex->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;
}
「STL详解」RB-tree 红黑树相关推荐
- sql server tcp 信号灯超时时间已到_「图文详解」TCP为啥要3次握手和4次挥手?3次挥手不行吗?...
原文链接:https://www.cnblogs.com/qdhxhz/p/8470997.html TCP有6种标示:SYN(建立联机) ACK(确认) PSH(传送) FIN(结束) RST(重置 ...
- [免费专栏] Android安全之数据存储与数据安全「详解」
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...
- 「视频直播技术详解」系列之六:现代播放器原理
关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...
- sscanf函数用法详解_「MOS考点详解」一道例题详解GETPIVOTDATA函数的用法
要点提示 GETPIVOTDATA函数是MOS Excel认证考试的重要考点.资深Office培训师谷月老师在此借助一道例题详细解读这个函数. 例题 在「按地区」工作表上的单元格 H3 中,使用 GE ...
- 【贪玩巴斯】数字信号处理Digital Signal Processing(DSP)——第三节「离散时间 系统 详解」2021-09-29
数字信号处理Digital Signal Processing(DSP)--离散时间系统的详解~ 1. 离散时间系统 1.输入-输出描述 2.系统状态决定因素 3.结构图表示(考点) 重点例题 1. ...
- [转]【C/C++】STL详解
转载备用,原创作者在文章结尾... 文章目录 概述 STL六大组件简介 三大组件介绍 1. 容器 2. 算法 3. 迭代器 常用容器 1. string容器 string容器基本概念 string ...
- 【C/C++学习】之STL详解
文章目录 概述 STL六大组件简介 三大组件介绍 容器 算法 迭代器 常用容器 1. string容器 string容器基本概念 string容器常用操作 2. vector容器 ...
- 【STL详解】stack
文章目录 前言 一.STL? 二.stack 1. stack的创建 2. stack相关方法 3. 如何对satck进行排序 前言 本篇文章将总结SLT - stack,以及其常用方法. 一.STL ...
- [转] C++ STL详解
转载自:https://www.cnblogs.com/CnZyy/p/3317999.html C++合理运用STL标准库是非常方便的,对数据结构和一些算法的学习也大有裨益. 事实上转载处也是转载自 ...
最新文章
- android网络编程之HttpUrlConnection的讲解--GET请求
- 小学生python入门-写给中小学老师们的Python入门指引
- 【转】Linux上安装使用SSH(ubunturedhat)
- Artifactory——启动错误[Artifactory failed to initialize: check Artifactory logs for errors.]解决方案
- php 对象 final,PHP7_OOP_对象重载以及魔术方法_对象遍历_final关键字
- hdu1521 指数型母函数
- wampServer2.1错误(Could not execute menu item (internal error)
- 大王——有趣干货集合
- 取消关机shutdown -c
- linux下安装gcc
- bzoj 1023: [SHOI2008]cactus仙人掌图(仙人掌求直径)
- 基于C#实现的个人日程管理系统
- java将html转为word文档,java html转换为word文档
- 【Office】Project2016 删除资源
- JDBC【数据库连接池、DbUtils框架、分页】
- Keil5使用第一步
- python爬虫学习:selenium(2)
- 用最“硬核”的青春感动中国,致敬军人!
- 别一想到搜索就用百度啦,推荐10个常用的垂直搜索引擎
- 叮咚买菜自建MongoDB上云实践
热门文章
- 做提高打字速度的练习后的漫谈
- python爬虫--爬取链家租房信息
- 手游冷知识丨为什么绝大多数手游不开放自由交易系统?
- Python获取win7下的CPU温度
- acmev2怎么用_lua-resty-acme: ACMEv2 客户端和 Let's Encrypt 证书的自动化管理
- 女双中后场常用技术及训练方法[技术解码]
- 20145240 《信息安全系统设计基础》第六周同学问题总结
- Git基础-查看、添加、删除远程仓库链接
- insert into bak select * from test会锁表吗
- PA,MIOU,FWIOU