一、红黑树定义

  • 红黑树(RB-tree)也是广泛应用的平衡二叉树
  • 红黑树必须满足下面的规则:
    • 1.每个节点不是红色就是黑色
    • 2.根节点为黑色
    • 3.如果节点为红色,那么子节点必须为黑(也就是说不能有两个节点连续为红色)
    • 4.任一节点到NULL(树尾端)的任何路径上,所含的黑色节点数目必须相同
  • 下图是一个图示。深色的为黑色,浅色的为红色

二、插入节点

新插入的节点必为红色

  • 如果插入前树非空,插入的新节点为黑色,肯定不满足上面的规则4
  • 如果插入前树非空,插入的新节点为红色,那么可能会不满足上面的规则3(如插入节点的父节点为红色),也可能满足上面的规则3(如插入节点的父节点为黑色)
  • 总结:我们应该将新插入的节点设置为红色,如果满足红黑树条件那么就插入成功;如果不满足红黑树条件那么可以通过改变颜色或旋转来调节不平衡

一些名字的规定

  • 假设:

    • 新节点为X
    • 父节点为P
    • 祖父节点为G
    • 伯父节点(父节点的兄弟节点)为S
    • 曾祖父节点为GG
  • 如果插入导致不平衡。那么:
    • 新插入的节点X为红色
    • 那么父节点P必为红色
    • 又因为插入前红黑树本身是平衡的,因此父节点P的父节点G也为黑色

状况①

  • 插入前:S为黑色且X为外侧插入
  • 调整平衡:我们先对P、G做一次单旋转,然后再改变P、G的颜色,就可以满足红黑树的规则3

  • 备注:此时可能会产生不平衡状态(高度相差1以上)。例如图中的A和B为NULL,而D和E不为NULL。这也没关系,因为红黑树的平衡性本来就比AVL-tree弱。然而RB-tree通常能够导致良好的平衡状态。经验总结,RB-tree的搜寻效率和AVL-tree几乎相等

状况②

  • 插入前:S为黑且X为内侧插入
  • 调整平衡:先对P、X做一次单旋转,并改变G、X的颜色,再将结果对G做一次单旋转,就可以满足红黑树的规则3

状况③

  • 插入前:S为红色且X为外侧插入
  • 调整平衡:先对P、G做一次单旋转,并改变X的颜色。此时如果GG为黑色,那么没有问题(如果GG为红色,那么就参阅下面的状况④)

状况④

  • 插入前:S为红色且X为外侧插入
  • 调整平衡:先对P、G做一次单旋转,并改变X的颜色。如果GG也为红色,那么需要持续向上做,直到不再有父子连续为红色位置

三、一个由上而下的程序

  • 为了避免上面状况④“父子节点都为红色”的情况持续向RB-tree的上层结构发展,形成处理时效率上的瓶颈,我们可以施行一个由上而下的程序:假设新增节点为A,那么就沿着A的路径,只要看到有某节点X的两个子节点都为红色,就把X改为红色,并把两个子节点改为黑色。如下图所示

  • 但是如果A的父节点P还为红色(注意,此时S绝不可能为红色),就得向状况①一样地做一次单旋转并改变颜色,或是像胡子那个框②一样地做一次双旋转并改变颜色
  • 在此之后,节点35的插入就变得简单了:要么直接插入,要么插入后再一次单旋转即可。如下图所示

四、RB-tree的节点设计

  • 红黑树有红黑二色,并且有用左右子节点和父节点
  • 下面是SGI STL的实现源码。为了有更大的弹性,节点分为两层实现。将后面介绍迭代器的时候将会显示节点双层结构和迭代器双层结构的关系

_Rb_tree_node_base、 _Rb_tree_node

  • _Rb_tree_node_base定义了节点的基本结构
  • _Rb_tree_node继承于_Rb_tree_node_base,并且定义了节点的值。这个就是红黑树节点的最终表示
//来自于stl_tree.h
typedef bool _Rb_tree_Color_type;
const _Rb_tree_Color_type _S_rb_tree_red = false;  //红色为0
const _Rb_tree_Color_type _S_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 _M_color;  //节点颜色_Base_ptr _M_parent;   //父节点_Base_ptr _M_left;     //左子节点_Base_ptr _M_right;    //右子节点//下面两个函数实现非常的简单//一直向左走,就会找到最小值static _Base_ptr _S_minimum(_Base_ptr __x){while (__x->_M_left != 0) __x = __x->_M_left;return __x;}//一直向右走,就会找到最大值static _Base_ptr _S_maximum(_Base_ptr __x){while (__x->_M_right != 0) __x = __x->_M_right;return __x;}
};
//来自于stl_tree.h
template <class _Value>
struct _Rb_tree_node : public _Rb_tree_node_base
{typedef _Rb_tree_node<_Value>* _Link_type;_Value _M_value_field; //节点值
};
  • 例如下图是一个红黑树节点结构,其值为10

五、RB-tree的迭代器

  • 为了有更大的弹性,SGI将RB-tree的迭代器实现为两层。这种设计理念和前面介绍的slist容器比较类似
  • 下图便是双层节点结构和双层迭代器结构之间的关系,其中主要意义是:
    • __rb_tree_node继承自__rb_tree_node_base
    • __rb_tree_iterator继承自__rb_tree_base_iterator,__rb_tree_iterator才是RB-tree最终使用到的迭代器

  • RB-tree迭代器属于双向迭代器,但不具备随机定位能力,其提领操作和成员访问操作与list十分类似
  • 注意:
    • RB-tree迭代器的前进操作operator++()调用的是基类中的increment()函数
    • RB-tree迭代器的后退操作operator--()调用的是基类中的decrement()函数
  • 前进或后退的行为依据二叉搜索树的节点排列规则,再加上实现上的某些特殊技巧

基类中的迭代器(__rb_tree_base_iterator)

//来自于stl_tree.h
struct _Rb_tree_base_iterator //基层迭代器
{typedef _Rb_tree_node_base::_Base_ptr _Base_ptr;typedef bidirectional_iterator_tag iterator_category;typedef ptrdiff_t difference_type;_Base_ptr _M_node; //用来与容器之间产生一个连结关系(make a reference)//以下其实可实现于operator++内,因为不会再有其他地方会调用此函数了void _M_increment(){//如果有右子节点(状况1)就向右走,然后一直往左子树走到底即时解答if (_M_node->_M_right != 0) {_M_node = _M_node->_M_right;while (_M_node->_M_left != 0)_M_node = _M_node->_M_left;}else { //没有右子节点(状况2),找出父节点,如果现行节点本身是个右子节点就一直上溯,直到“不为右子节点”为止_Base_ptr __y = _M_node->_M_parent;while (_M_node == __y->_M_right) {_M_node = __y;__y = __y->_M_parent;}//若此时的右子节点不等于此时的父节点,此时的父节点(状况3),此时的父节点即为解答。否则此时的node为解答(状况4)if (_M_node->_M_right != __y)_M_node = __y;}/*注意:以上判断“若此时的右子节点不等于此时的父节点”,是为了应付的一种特殊情况:我们想要寻找根节点的下一节点,而恰巧根节点无右子节点,当然,以上特殊做法必须配合红黑树根节点与特殊的header之间的关系*/}//以下其实可实现于operator-内,因为不会再有其他地方会调用此函数了void _M_decrement(){//如果是红街店,且父节点的父节点等于自己(状况1)右子节点即为解答if (_M_node->_M_color == _S_rb_tree_red &&_M_node->_M_parent->_M_parent == _M_node)_M_node = _M_node->_M_right;//以上情况发生于node为header时(即node为end()时)//注意,header的右子节点即mostright,指向整棵树的max节点//如果有左子节点(状况2)令y指向左子节点,当y有右子节点时,一直往右子节点走到底最后即为答案else if (_M_node->_M_left != 0) {_Base_ptr __y = _M_node->_M_left;while (__y->_M_right != 0)__y = __y->_M_right;_M_node = __y;}//即非根节点,也没有左子节点(状况3),找出父节点,当现行节点身为左子节点,一直交替向上走,知道现行节点不为左子节点else {_Base_ptr __y = _M_node->_M_parent;while (_M_node == __y->_M_left) {_M_node = __y;__y = __y->_M_parent;}_M_node = __y; //此时的父节点即为答案}}
};
  • 针对increment()和decrement()两个函数,比较难理解的是increment的状况4和decrement状况1。它们分别发生于下图所展示的状态下

派生类中的迭代器(__rb_tree_iterator)

//来自于stl_tree.h
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) { _M_node = __x; }_Rb_tree_iterator(const iterator& __it) { _M_node = __it._M_node; }reference operator*() const { return _Link_type(_M_node)->_M_value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATORpointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */_Self& operator++() { _M_increment(); return *this; }_Self operator++(int) {_Self __tmp = *this;_M_increment();return __tmp;}_Self& operator--() { _M_decrement(); return *this; }_Self operator--(int) {_Self __tmp = *this;_M_decrement();return __tmp;}
};

六、RB-tree的数据结构

  • 下面是RB-tree的定义(_Rb_tree)。可以看到其中有:

    • _Rb_tree继承于_Rb_tree_base
    • 专属的空间配置器,每次用来配置一个节点的大小
    • 也可以看到各种型别定义,用来维护整棵RB-tree的三种数据(其中有个仿函数,functor,用来表现节点的大小比较方式)
    • 以及一些成员函数的定义和声明
//来自于stl_tree.h
template <class _Tp, class _Alloc>
struct _Rb_tree_base
{typedef _Alloc allocator_type;allocator_type get_allocator() const { return allocator_type(); }_Rb_tree_base(const allocator_type&) : _M_header(0) { _M_header = _M_get_node(); }~_Rb_tree_base() { _M_put_node(_M_header); }protected:_Rb_tree_node<_Tp>* _M_header;typedef simple_alloc<_Rb_tree_node<_Tp>, _Alloc> _Alloc_type;_Rb_tree_node<_Tp>* _M_get_node(){ return _Alloc_type::allocate(1); }void _M_put_node(_Rb_tree_node<_Tp>* __p){ _Alloc_type::deallocate(__p, 1); }
};template <class _Key, class _Value, class _KeyOfValue, class _Compare,class _Alloc = __STL_DEFAULT_ALLOCATOR(_Value) >
class _Rb_tree : protected _Rb_tree_base<_Value, _Alloc> {typedef _Rb_tree_base<_Value, _Alloc> _Base;
protected:typedef _Rb_tree_node_base* _Base_ptr;typedef _Rb_tree_node<_Value> _Rb_tree_node;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;typedef typename _Base::allocator_type allocator_type;allocator_type get_allocator() const { return _Base::get_allocator(); }protected:
#ifdef __STL_USE_NAMESPACESusing _Base::_M_get_node;using _Base::_M_put_node;using _Base::_M_header;
#endif /* __STL_USE_NAMESPACES */protected:_Link_type _M_create_node(const value_type& __x){_Link_type __tmp = _M_get_node(); //配置空间__STL_TRY {construct(&__tmp->_M_value_field, __x); //构造内容}__STL_UNWIND(_M_put_node(__tmp));return __tmp;}_Link_type _M_clone_node(_Link_type __x) //复制一个节点(值和颜色){_Link_type __tmp = _M_create_node(__x->_M_value_field);__tmp->_M_color = __x->_M_color;__tmp->_M_left = 0;__tmp->_M_right = 0;return __tmp;}void destroy_node(_Link_type __p){destroy(&__p->_M_value_field); //析构内容_M_put_node(__p); //释放内存}protected:size_type _M_node_count; // keeps track of size of tree 追踪记录树的大小(节点数量)_Compare _M_key_compare; //节点间的键值大小比较准则,应该会是个function object//以下3个函数用来方便取得header的成员_Link_type& _M_root() const { return (_Link_type&) _M_header->_M_parent; }_Link_type& _M_leftmost() const { return (_Link_type&) _M_header->_M_left; }_Link_type& _M_rightmost() const { return (_Link_type&) _M_header->_M_right; }//以下6个函数用来方便取得节点x的成员static _Link_type& _S_left(_Link_type __x){ return (_Link_type&)(__x->_M_left); }static _Link_type& _S_right(_Link_type __x){ return (_Link_type&)(__x->_M_right); }static _Link_type& _S_parent(_Link_type __x){ return (_Link_type&)(__x->_M_parent); }static reference _S_value(_Link_type __x){ return __x->_M_value_field; }static const _Key& _S_key(_Link_type __x){ return _KeyOfValue()(_S_value(__x)); }static _Color_type& _S_color(_Link_type __x){ return (_Color_type&)(__x->_M_color); }//以下6个函数用来方便取得节点x的成员static _Link_type& _S_left(_Base_ptr __x){ return (_Link_type&)(__x->_M_left); }static _Link_type& _S_right(_Base_ptr __x){ return (_Link_type&)(__x->_M_right); }static _Link_type& _S_parent(_Base_ptr __x){ return (_Link_type&)(__x->_M_parent); }static reference _S_value(_Base_ptr __x){ return ((_Link_type)__x)->_M_value_field; }static const _Key& _S_key(_Base_ptr __x){ return _KeyOfValue()(_S_value(_Link_type(__x)));} static _Color_type& _S_color(_Base_ptr __x){ return (_Color_type&)(_Link_type(__x)->_M_color); }//求取极大值和极小值。node class有实现此功能,交给他们完成即可static _Link_type _S_minimum(_Link_type __x) { return (_Link_type)  _Rb_tree_node_base::_S_minimum(__x); }static _Link_type _S_maximum(_Link_type __x){ return (_Link_type) _Rb_tree_node_base::_S_maximum(__x); }public:typedef _Rb_tree_iterator<value_type, reference, pointer> iterator;typedef _Rb_tree_iterator<value_type, const_reference, const_pointer> const_iterator;#ifdef __STL_CLASS_PARTIAL_SPECIALIZATIONtypedef reverse_iterator<const_iterator> const_reverse_iterator;typedef reverse_iterator<iterator> reverse_iterator;
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */typedef reverse_bidirectional_iterator<iterator, value_type, reference,difference_type>reverse_iterator; typedef reverse_bidirectional_iterator<const_iterator, value_type,const_reference, difference_type>const_reverse_iterator;
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */ private:iterator _M_insert(_Base_ptr __x, _Base_ptr __y, const value_type& __v);_Link_type _M_copy(_Link_type __x, _Link_type __p);void _M_erase(_Link_type __x);public:// allocation/deallocation_Rb_tree(): _Base(allocator_type()), _M_node_count(0), _M_key_compare(){ _M_empty_initialize(); }_Rb_tree(const _Compare& __comp): _Base(allocator_type()), _M_node_count(0), _M_key_compare(__comp) { _M_empty_initialize(); }_Rb_tree(const _Compare& __comp, const allocator_type& __a): _Base(__a), _M_node_count(0), _M_key_compare(__comp) { _M_empty_initialize(); }_Rb_tree(const _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>& __x) : _Base(__x.get_allocator()),_M_node_count(0), _M_key_compare(__x._M_key_compare){ if (__x._M_root() == 0)_M_empty_initialize();else {_S_color(_M_header) = _S_rb_tree_red;_M_root() = _M_copy(__x._M_root(), _M_header);_M_leftmost() = _S_minimum(_M_root());_M_rightmost() = _S_maximum(_M_root());}_M_node_count = __x._M_node_count;}~_Rb_tree() { clear(); }_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>& operator=(const _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>& __x);private:void _M_empty_initialize() {_S_color(_M_header) = _S_rb_tree_red; // 令header为红色,用来区分header和root,在iterator.operator++之中_M_root() = 0;_M_leftmost() = _M_header; //令header的左子节点为自己_M_rightmost() = _M_header;//令header的右子节点为自己}public:    // accessors:_Compare key_comp() const { return _M_key_compare; }iterator begin() { return _M_leftmost(); }const_iterator begin() const { return _M_leftmost(); }iterator end() { return _M_header; }const_iterator end() const { return _M_header; }reverse_iterator rbegin() { return reverse_iterator(end()); }const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }reverse_iterator rend() { return reverse_iterator(begin()); }const_reverse_iterator rend() const { return const_reverse_iterator(begin());} bool empty() const { return _M_node_count == 0; }size_type size() const { return _M_node_count; }size_type max_size() const { return size_type(-1); }void swap(_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>& __t) {__STD::swap(_M_header, __t._M_header);__STD::swap(_M_node_count, __t._M_node_count);__STD::swap(_M_key_compare, __t._M_key_compare);}public:// insert/erase//将x插入到RB-tree中(保持节点值第一无二)pair<iterator,bool> insert_unique(const value_type& __x);将x插入到RB-tree中(允许节点值重复)iterator insert_equal(const value_type& __x);iterator insert_unique(iterator __position, const value_type& __x);iterator insert_equal(iterator __position, const value_type& __x);#ifdef __STL_MEMBER_TEMPLATES  template <class _InputIterator>void insert_unique(_InputIterator __first, _InputIterator __last);template <class _InputIterator>void insert_equal(_InputIterator __first, _InputIterator __last);
#else /* __STL_MEMBER_TEMPLATES */void insert_unique(const_iterator __first, const_iterator __last);void insert_unique(const value_type* __first, const value_type* __last);void insert_equal(const_iterator __first, const_iterator __last);void insert_equal(const value_type* __first, const value_type* __last);
#endif /* __STL_MEMBER_TEMPLATES */void erase(iterator __position);size_type erase(const key_type& __x);void erase(iterator __first, iterator __last);void erase(const key_type* __first, const key_type* __last);void clear() {if (_M_node_count != 0) {_M_erase(_M_root());_M_leftmost() = _M_header;_M_root() = 0;_M_rightmost() = _M_header;_M_node_count = 0;}}      public:// set operations:iterator find(const key_type& __x);const_iterator find(const key_type& __x) const;size_type count(const key_type& __x) const;iterator lower_bound(const key_type& __x);const_iterator lower_bound(const key_type& __x) const;iterator upper_bound(const key_type& __x);const_iterator upper_bound(const key_type& __x) const;pair<iterator,iterator> equal_range(const key_type& __x);pair<const_iterator, const_iterator> equal_range(const key_type& __x) const;public:// Debugging.bool __rb_verify() const;
};    

七、RB-tree的数据结构的构造与内存管理

空间配置器_Alloc_type

  • 下面是RB-tree所定义的专属空间配置器_Alloc_type,每次可恰恰配置一个节点,它所使用的simple_alloc<>定义在前面的文章
template <class _Tp, class _Alloc>
struct _Rb_tree_base
{
//...
protected:_Rb_tree_node<_Tp>* _M_header;typedef simple_alloc<_Rb_tree_node<_Tp>, _Alloc> _Alloc_type;
//...
};
  • 前面的代码中也显示了节点相关的函数,如get_node()、put_node()、create_node()、clone_node()、destory_node()
  • RB-tree的构造方式有两种:
    • 一种是以现有的RB-tree复制一个新的RB-tree
    • 另一种是产生一棵空树

演示说明

  • 下面定义一个红黑树(备注:RB-tree并未明列于STL标准规则之中,因为我们了解SGI STL的底层,因此能这么用):
rb_tree<int,int,identity<int>,less<int> > itree; 
  • 这行代码分别指定了节点的键值、实值、大小比较标准,然后调用RB-tree的默认构造函数。默认构造函数如下
_Rb_tree(const _Compare& __comp): _Base(allocator_type()), _M_node_count(0), _M_key_compare(__comp) { _M_empty_initialize(); }
  •  _M_empty_initialize的实现如下:
void _M_empty_initialize() {_S_color(_M_header) = _S_rb_tree_red; // 令header为红色,用来区分header和root,在iterator.operator++之中_M_root() = 0;_M_leftmost() = _M_header; //令header的左子节点为自己_M_rightmost() = _M_header;//令header的右子节点为自己}

header节点

  • 我们知道,树结构的各种操作最需要注意的就是边界情况的发生,也就是走到根节点时要有特殊处理。为了简化处理,SGI STL特别为根节点再设计一个父节点,名为header,并令其初始状态如下图所示:

  • 接下来,每当插入新节点时,不但要依照RB-tree的规则来调整,并且维护header的正确性,使其父节点指向根节点,左子节点指向最小节点,右子节点指向最大节点。节点的插入所带来的的影响,是下面要描述的重点

八、RB-tree的元素操作

  • 接下来主要谈元素(节点)的插入和查找
  • RB-tree提供两种类型的插入操作:
    • insert_unique():表示被插入节点的键值(key)在整棵树中必须独一无二(因此,如果树中已存在相同的键值,插入操作就不会真正进行)
    • insert_equal():表示被插入节点的键值在整棵树中可以重复。因此,无论如何插入都会成功(除非空间不足)
    • 这两个函数也有数个版本,下面以最简单的版本作为说明对象(单一参数,用以表现将被插入的节点实值(values))。注意,虽然只指定实值,但是RB-tree一开始即要求用户必须明确设定所谓的KeyOfValue仿函数,因此,从实值(value)中取出键值(key)是毫无问题的

元素插入操作(insert_equal())

//插入新值:节点允许重复
//返回是一个迭代器,指向于新增节点
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 = _M_header;_Link_type __x = _M_root(); //从根节点开始while (__x != 0) { //往下寻找适当的插入点__y = __x;__x = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)) ? _S_left(__x) : _S_right(__x);}return _M_insert(__x, __y, __v);
}

元素插入操作(insert_unique())

//插入新值:节点键值不允许重复,若重复则插入无效
//返回值是个pair。pair中的第一个元素是个RB-tree迭代器,指向于新增节点。第二个元素表示插入成功
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 = _M_header;_Link_type __x = _M_root(); //从根节点开始bool __comp = true;while (__x != 0) {  //从根节点开始,往下寻找适当的插入点__y = __x;__comp = _M_key_compare(_KeyOfValue()(__v), _S_key(__x));__x = __comp ? _S_left(__x) : _S_right(__x);} //while结束之后,y所指即插入点的父节点(此时的它必为叶节点)iterator __j = iterator(__y);   if (__comp)  //离开时while后,comp为真(表示遇“大”,将插入于左侧)if (__j == begin())     return pair<iterator,bool>(_M_insert(__x, __y, __v), true);else--__j;//小于新值(插入右侧)if (_M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)))return pair<iterator,bool>(_M_insert(__x, __y, __v), true);//运行到这里,表示新值一定与树中键值重复,那么就不插入return pair<iterator,bool>(__j, false);
}

真正的插入执行程序(_M_insert())

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>::_M_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;if (__y == _M_header || __x != 0 || _M_key_compare(_KeyOfValue()(__v), _S_key(__y))) {__z = _M_create_node(__v);_S_left(__y) = __z;               // also makes _M_leftmost() = __z //    when __y == _M_headerif (__y == _M_header) {_M_root() = __z;_M_rightmost() = __z;}else if (__y == _M_leftmost())_M_leftmost() = __z;   // maintain _M_leftmost() pointing to min node}else {__z = _M_create_node(__v);_S_right(__y) = __z;if (__y == _M_rightmost())_M_rightmost() = __z;  // maintain _M_rightmost() pointing to max node}_S_parent(__z) = __y;_S_left(__z) = 0;_S_right(__z) = 0;_Rb_tree_rebalance(__z, _M_header->_M_parent);++_M_node_count;return iterator(__z);
}

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

  • 任何插入操作,插入完毕之后都要做一次调整操作,将树的状态调整到符合红黑树的要求
  • _Rb_tree_rebalance函数:是一个全局函数,用来对RB-tree进行调整
    • 下面的函数就是上面所说的“由上而下的程序”。从源代码可以看到,有些时候只需调整节点颜色,有些时候需要做单旋转或双旋转;有些时候要左旋,有些时候要右旋
//全局函数,重新令树平衡(改变颜色及旋转)
//参数1位新增节点,参数2位root
inline void
_Rb_tree_rebalance(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{__x->_M_color = _S_rb_tree_red; //新增节点必为红while (__x != __root && __x->_M_parent->_M_color == _S_rb_tree_red) {//父节点为红if (__x->_M_parent == __x->_M_parent->_M_parent->_M_left) { //父节点为祖父节点的左孩子_Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_right;//令y为伯父节点if (__y && __y->_M_color == _S_rb_tree_red) { //伯父节点存在,且为红__x->_M_parent->_M_color = _S_rb_tree_black; //更改父节点为黑__y->_M_color = _S_rb_tree_black; //更高伯父节点为红__x->_M_parent->_M_parent->_M_color = _S_rb_tree_red; //更高祖父节点为红__x = __x->_M_parent->_M_parent;}else {//无伯父节点,或伯父节点为红if (__x == __x->_M_parent->_M_right) { //新节点为父节点的右孩子__x = __x->_M_parent;_Rb_tree_rotate_left(__x, __root); //第一参数为左旋点}__x->_M_parent->_M_color = _S_rb_tree_black; //改变颜色__x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;_Rb_tree_rotate_right(__x->_M_parent->_M_parent, __root);//第一参数为右旋点}}else {//父节点为祖父节点的右孩子_Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_left;//令y为伯父节点if (__y && __y->_M_color == _S_rb_tree_red) {//有伯父节点,且为红__x->_M_parent->_M_color = _S_rb_tree_black;//更改父节点为黑__y->_M_color = _S_rb_tree_black;//更改伯父节点为黑__x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;//更改祖父节点为红__x = __x->_M_parent->_M_parent;//准备继续向上层检查}else { //无伯父节点,或伯父节点为黑if (__x == __x->_M_parent->_M_left) { //如果新节点为父节点的左孩子__x = __x->_M_parent;_Rb_tree_rotate_right(__x, __root);//第一参数为右旋点}__x->_M_parent->_M_color = _S_rb_tree_black;//更改颜色__x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;_Rb_tree_rotate_left(__x->_M_parent->_M_parent, __root);//第一参数为左旋点}}}__root->_M_color = _S_rb_tree_black;//根节点永远为黑
}
  • _Rb_tree_rotate_left:左旋函数
//全局函数
//新节点必为红节点,如果插入之处父节点也为红色,就违反了规则,此时需要做树形旋转(及颜色改变,颜色改变不是在这里)
inline void
_Rb_tree_rotate_left(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{//x为旋转点_Rb_tree_node_base* __y = __x->_M_right; //令y为旋转点的右子节点__x->_M_right = __y->_M_left;if (__y->_M_left !=0)__y->_M_left->_M_parent = __x; //别忘了设定父节点__y->_M_parent = __x->_M_parent;//令y完全顶替x的地位(必须将x对齐父节点的关系完全接收过来)if (__x == __root) //x为根节点__root = __y;else if (__x == __x->_M_parent->_M_left) //x为其父节点的左子节点__x->_M_parent->_M_left = __y;else__x->_M_parent->_M_right = __y; //x为其父节点的右子节点__y->_M_left = __x;__x->_M_parent = __y;
}
  • _Rb_tree_rotate_right:右旋函数
//全局函数
//新节点必为红节点,如果插入之处父节点也为红色,就违反了规则,此时需要做树形旋转(及颜色改变,颜色改变不是在这里)
inline void
_Rb_tree_rotate_right(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{//y为旋转点_Rb_tree_node_base* __y = __x->_M_left; //令y为旋转点的左子节点__x->_M_left = __y->_M_right;if (__y->_M_right != 0)__y->_M_right->_M_parent = __x; //别忘了设定父节点__y->_M_parent = __x->_M_parent;//令y完全顶替x的地位(必须将x对齐父节点的关系完全接收过来)if (__x == __root)//x为根节点__root = __y;else if (__x == __x->_M_parent->_M_right)//x为其父节点的右子节点__x->_M_parent->_M_right = __y;else//x为其父节点的左子节点__x->_M_parent->_M_left = __y;__y->_M_right = __x;__x->_M_parent = __y;
}
  • 下面是一个演示案例

  • 下图是上面那个演示案例的完整过程

元素的搜索(find())

  • RB-tree是一个二叉搜索树,因此搜寻才是其最拿手的地方。下面是RB-tree提供的find函数
//寻找树中是否有键值为k的节点
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>::find(const _Key& __k)
{_Link_type __y = _M_header;      // Last node which is not less than __k. _Link_type __x = _M_root();      // Current node. while (__x != 0) //以下,key_compare是节点键值大小比较准则,应该会是个function objectif (!_M_key_compare(_S_key(__x), __k))//进行到这里,表示x键值大于k,遇到大值就向左走__y = __x, __x = _S_left(__x);//注意语法else//进行到这里,表示x键值小于k,遇到大值就向右走__x = _S_right(__x); iterator __j = iterator(__y);   return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;
}

stl源码剖析09——RB-Tree(红黑树)相关推荐

  1. STL源码剖析 关联式容器 红黑树

    概念 红黑树不仅仅是一个二叉树,必须满足如下条件 1,每个节点不是红色就是黑色 (深色底纹为黑色,浅色底纹为红色) 2,根节点是黑色的 3,如果节点为红,其子节点必须为黑色的 4,任一节点至NULL( ...

  2. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

  3. STL源码剖析 map

    所有元素会根据元素的键值自动被排序 元素的类型是pair,同时拥有键值和实值:map不允许两个元素出现相同的键值 pair 代码 template <class T1,class T2> ...

  4. C++ STL源码剖析 笔记

    写在前面 记录一下<C++ STL源码剖析>中的要点. 一.STL六大组件 容器(container): 各种数据结构,用于存放数据: class template 类泛型: 如vecto ...

  5. STL(C++标准库,体系结构及其内核分析)(STL源码剖析)(更新完毕)

    文章目录 介绍 Level 0:使用C++标准库 0 STL六大部件 0.1 六大部件之间的关系 0.2 复杂度 0.3 容器是前闭后开(左闭右开)区间 1 容器的结构与分类 1.1 使用容器Arra ...

  6. STL源码剖析面试问题

    当vector的内存用完了,它是如何动态扩展内存的?它是怎么释放内存的?用clear可以释放掉内存吗?是不是线程安全的? vector内存用完了,会以当前size大小重新申请2* size的内存,然后 ...

  7. STL 源码剖析 heap堆

    heap不属于STL容器的组件,属于幕后角色,是priority_queue的助手 priority_queue 允许用户以任何次序将任何元素推入容器内,但是取出的时候需要从优先级最高(也就是数值最高 ...

  8. 《STL源码剖析》相关面试题总结

    一.STL简介 STL提供六大组件,彼此可以组合套用: 容器 容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template. 算法 各种常 ...

  9. STL源码剖析(十三)关联式容器之rb_tree

    STL源码剖析(十三)关联式容器之rb_tree 文章目录 STL源码剖析(十三)关联式容器之rb_tree 一.rb_tree的数据结构 二.rb_tree的迭代器 三.rb_tree的操作 3.1 ...

最新文章

  1. CSP 201912-1 报数 python实现
  2. 三种字符编码:ASCII、Unicode和UTF-8
  3. Machine Learning week 2 quiz: Linear Regression with Multiple Variables
  4. 【转】Dynamics 365中配置和使用文件夹级别的跟踪(folder-level tracking)
  5. python使用md5加密_如何使用Python创建自己的加密货币
  6. Fedora配置清华镜像源或者阿里云镜像源
  7. layui 读取本地excel内容_Python操作Excel基础(1)
  8. 简便无刷新文件上传系统
  9. python两组数的差异_Python中两个日期之间的差异
  10. nodejs做中间层_nodejs做中间层,向后端取数据
  11. 怎么检测mysql查询是否慢_MySQL慢查询查找和调优测试
  12. Netty的并发编程实践4:线程安全类的应用
  13. Qt QLabel无法显示符号
  14. 北大才女文章 《卖米》感动人心
  15. 2020.9.2丨个体重测序、KASP分型产品介绍
  16. win下处理器组概念
  17. 基于ESB权限初始化流程开发总结
  18. 专科计算机教育的现状,探析高职计算机专业英语教学现状
  19. 可折叠的table表格
  20. 用python制作文字特效

热门文章

  1. 计算机网络思维导图(零基础--思维导图详细版本及知识点)
  2. windows10新版java环境配置2022年4月21H1版本
  3. Android kotlin使用id直接做view的引用
  4. HackTheBox –Craft实战
  5. javascript 代码中的“use strict“;是什么意思
  6. java 注解处理器的作用_Java注解处理器
  7. 怎么写经济学论文的导言和文献综述
  8. 简要描述如何安装配置apache的一个开源Hadoop
  9. BP神经网络设计与实现
  10. Linux Mint 18安装sougou拼音输入法