STL源碼分析--rbtree

更多精彩內容,請關注微信公衆號:後端技術小屋node

1 相關頭文件

tree.h
stl_tree.h 
map.h
stl_map.h
set.h
stl_set.h

2 紅黑樹的實現

首先重溫下紅黑樹的定義。同時知足如下條件的二叉樹纔可稱之爲紅黑樹:算法

  1. 全部節點非紅即黑
  2. 根節點爲紅色。
  3. 全部葉子節點爲黑色。
  4. 若是一個節點爲紅色,那麼它的子節點爲黑色。
  5. 從任意給定節點向下到達葉子節點,通過相同數量的黑色節點。

2.1 紅黑樹的節點

首先用bool定義了紅黑樹節點的兩種顏色:後端

typedef bool _Rb_tree_Color_type;
const _Rb_tree_Color_type _S_rb_tree_red = false;
const _Rb_tree_Color_type _S_rb_tree_black = true;

其次定義節點,_Rb_tree_node_base定義了紅黑樹節點的基本結構,_Rb_tree_node在其基礎上加上了數據字段_M_value_field微信

struct _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;     // 指向右節點
}


template <class _Value>
struct _Rb_tree_node : public _Rb_tree_node_base
{
  typedef _Rb_tree_node<_Value>* _Link_type;
  _Value _M_value_field;   // 節點值
};

2.2 紅黑樹的結構

2.2.1 _Rb_tree_base

_Rb_tree_base定義了紅黑樹基本的數據結構。
從代碼中看到,其模板參數中_Tp表示紅黑樹節點中數據部分的類型, _Alloc表示內存分配器類類型。在數據結構上,紅黑樹由一個根節點_M_header組成。_M_get_node/_M_put_node分別申請和釋放節點內存。數據結構

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); }
};

2.2.2 _Rb_tree

_Rb_tree派生於_Rb_tree_base,在其基礎上增長了紅黑樹增刪改查等接口。_Rb_tree中模板參數衆多,且與STL源碼分析--hashtable中hashtable的模板參數類似:_Key表示紅黑樹中key(用於搜索節點)的類型,_Value表示紅黑樹節點中數據字段的類型,_KeyOfValue爲函數對象,用於從_Value對象中抽取出key, _Compare爲函數對象類型,用於比較兩個key的大小,由於紅黑樹中全部節點必須按key值有序放置。_Alloc爲內存分配器類型,缺省爲STL默認內存分配器(見STL源碼分析--內存分配器)app

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;
  ...
}

至於_Rb_tree中的增刪查改操做,可直接參考《算法導論》中相關章節,這裏略過。less

2.3 紅黑樹的迭代器

紅黑樹中的迭代器屬於雙向迭代器,既可自增1,又可自減1(見STL源碼分析--iterator)函數

_Rb_tree_base_iterator包含了一個指向紅黑樹節點的指針,表示迭代器的當前位置。另外還
定義了紅黑樹迭代器的部分基本類型(iterator_categorydifference_type)、自增自減操做源碼分析

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; 
  ...
}

_Rb_tree_iterator重載了自增自減操做符,分別調用了基類_Rb_tree_base_iterator中的_M_increment_M_decrement函數。this

template <class _Value, class _Ref, class _Ptr>
struct _Rb_tree_iterator : public _Rb_tree_base_iterator 
{
  ...
  _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;
  }
  ...
}

3 set/map/multiset/multimap與紅黑樹的關係

set/map/multiset/multimap都包含_Rb_tree。在使用上,set/multiset只存key,沒有value,map/multimap中key和value成對出現;set/map中不容許重複key存在,multiset/multimap則容許多個相同key的存在。接下來帶着這些問題過下相關代碼:

3.1 map

map中key的類型爲_Key, value的類型爲_Tp。對應的,紅黑樹節點中數據字段類型爲pair<const _Key, _Tp>_KeyOfValue_Select1st<pair<const _Key, _Tp>>,印證了map中key/value成對出現的說法。

另外key的比較函數對象爲less<_Key>,當左值小於右值時返回true。有了_Compare,可推斷:

  • key1 == key2 等效於 ! _Compare()(key1, key2) && !_Compare()(key2, key1)
  • key1 > key2 等效於 _Compare()(key2, key1)
template <class _Key, class _Tp, 
          class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class map; 

// _Key 爲鍵值類型,_Tp爲實值類型。
template <class _Key, class _Tp, class _Compare, class _Alloc>
class map {
public:
  typedef _Key                  key_type;  // 鍵值類型
  typedef _Tp                   data_type; // 數據類型
  typedef _Tp                   mapped_type;
  typedef pair<const _Key, _Tp> value_type;    // 元素類型(鍵值/實值)
  typedef _Compare              key_compare;   // 鍵值比較函數
  ...

private:
  // 如下定義表述類型(representation type).以map元素類型(一個pair)的第一類型,
  // 做爲RB-tree節點的鍵值類型
  typedef _Rb_tree<key_type, value_type, 
                   _Select1st<value_type>, key_compare, _Alloc> _Rep_type;
  _Rep_type _M_t;  // red-black tree representing map
  ...
}

map的insert接口代碼以下,其調用了_rb_tree::insert_unique函數,其保證了若是有同值key存在,則不插入任何節點。

pair<iterator,bool> insert(const value_type& __x) 
    { return _M_t.insert_unique(__x); }

至於set/multiset/multimap,留給聰明的讀者自行分析。

推薦閱讀

更多精彩內容,請掃碼關注微信公衆號:後端技術小屋。若是以爲文章對你有幫助的話,請多多分享、轉發、在看。
二維碼

相關文章
相關標籤/搜索