STL源碼剖析(set/map)


SGI STL中set/map底層都是通過RB-tree實現的。

首先看看RB-tree結點的定義

 1 typedef bool __rb_tree_color_type;
 2 const __rb_tree_color_type __rb_tree_red = false;
 3 const __rb_tree_color_type __rb_tree_black = true;
 4 
 5 // 結點的基類
 6 struct __rb_tree_node_base
 7 {
 8     typedef __rb_tree_color_type color_type;
 9     typedef __rb_tree_node_base* base_ptr;
10 
11     // 關鍵的4個域
12     color_type color; 
13     base_ptr parent;
14     base_ptr left;
15     base_ptr right;
16     
17     // 返回極值
18     static base_ptr minimum(base_ptr x)
19    {
20         while (x->left != 0) x = x->left;
21         return x;
22     }
23 
24     static base_ptr maximum(base_ptr x)
25     {
26         while (x->right != 0) x = x->right;
27         return x;
28      }
29 }
30 
31 // 多了一個value域
32 template <class Value>
33 struct __rb_tree_node : public __rb_tree_node_base
34 {
35     typedef __rb_tree_node<Value>* link_type;
36     Value value_field;
37 };
View Code

下圖是RB-tree結點跟其迭代器的關系

重點看看__rb_tree_iterator的operator++跟operator--,

實際是調用__rb_tree_base_iterator的increment跟decrement。

可以看出迭代器前移/后移的時候會按key的順序找到下一個/上一個結點。

(set/map<...>::begin()會返回RB-tree中key最小的結點,因此使用operator++遍歷會按key的順序從小到大遍歷結點)

 1 void increment()
 2 {
 3     if (node->right != 0) {
 4         node = node->right;
 5         while (node->left != 0)
 6             node = node->left;
 7     }
 8     else {
 9         base_ptr y = node->parent;
10         while (node == y->right) {
11             node = y;
12             y = y->parent;
13         }
14         if (node->right != y)
15             node = y;
16     }
17 }
18 
19 void decrement()
20 {
21     if (node->color == __rb_tree_red &&
22         node->parent->parent == node)
23         node = node->right;
24     else if (node->left != 0) {
25         base_ptr y = node->left;
26         while (y->right != 0)
27             y = y->right;
28         node = y;
29     }
30     else {
31         base_ptr y = node->parent;
32         while (node == y->left) {
33             node = y;
34             y = y->parent;
35         }
36         node = y;
37     }
38 }
View Code

 

SGI STL中的rb_tree用了一個小trick,就是使用了一個header結點,用來代表整個rb_tree。

該結點與root結點互為父結點,該結點的left指向最左(key最小)的結點,right指向最右(key最大)的結點。

 

除此之外,該rb_tree的實現跟普通的紅黑樹類似,詳情可以查看:http://www.cnblogs.com/runnyu/p/4679279.html。

 1 template <class Key, class Value, class KeyOfValue, class Compare,
 2           class Alloc = alloc>
 3 class rb_tree {
 4     // rb_tree的基本定義
 5 protected:
 6     typedef __rb_tree_node_base* base_ptr;
 7     typedef __rb_tree_node<Value> rb_tree_node;
 8     typedef simple_alloc<rb_tree_node, Alloc> rb_tree_node_allocator;
 9 public:
10     typedef Key key_type;
11     typedef Value value_type;
12     typedef value_type* pointer;
13     typedef value_type& reference;
14     typedef rb_tree_node* link_type;
15     typedef size_t size_type;
16     typedef ptrdiff_t difference_type;
17 
18     typedef __rb_tree_iterator<value_type, reference, pointer> iterator;
19 
20     link_type header;
21     // ...
22     
23     // 主要接口
24     iterator begin() { return leftmost(); } // 返回最左邊的結點(最小key)
25     iterator end() { return header; }
26     
27     iterator insert_equal(const value_type& x); // 插入元素 並允許鍵值相同
28     pair<iterator,bool> insert_unique(const value_type& x); // 插入元素 鍵值是獨一無二的
29     
30 };
View Code

 

set/multiset

有了rb_tree,set/multiset的實現也只是調用rb_tree的接口而已。

其中set跟multiset不一樣的是,set插入的時候調用的是insert_unique(),而multiset調用的是insert_equal()。

下面是給出set的基本定義:

 1 template <class Key, class Compare = less<Key>, class Alloc = alloc>
 2 class set {
 3 public:
 4     typedef Key key_type;
 5     typedef Key value_type;  // 使用的value類型跟key一樣
 6     typedef Compare key_compare;
 7     typedef Compare value_compare;
 8 private:
 9     typedef rb_tree<key_type, value_type, 
10                   identity<value_type>, key_compare, Alloc> rep_type;
11     rep_type t;
12 public:
13     // 接口的實現只是對rb_tree的封裝  不一一列舉了
14     iterator begin() const { return t.begin(); }
15     iterator end() const { return t.end(); }
16     pair<iterator,bool> insert(const value_type& x) { 
17         pair<typename rep_type::iterator, bool> p = t.insert_unique(x); 
18         return pair<iterator, bool>(p.first, p.second);
19     }
20     // ...
21 };
View Code

 

map/multimap

map/mulitmap的實現也是通過調用rb_tree的接口。

map/mulitmap不一樣的是,map插入的時候調用的是insert_unique(),而multimap調用的是insert_equal()。

下面是給出map的基本定義:

 1 template <class Key, class T, class Compare = less<Key>, class Alloc 
 2 class map {
 3 public:
 4     typedef Key key_type;
 5     typedef T data_type;
 6     typedef T mapped_type;
 7     typedef pair<const Key, T> value_type; // 在rb_tree中value的類型是pair
 8     typedef Compare key_compare;
 9 private:
10     // select1st直接return T.first 用於rb_tree取到key進行比較大小
11     typedef rb_tree<key_type, value_type, 
12                   select1st<value_type>, key_compare, Alloc> rep_type;
13     rep_type t;
14     // ...
15     
16     // 接口只是對rb_tree的封裝  就不一一列舉了
17     iterator begin() { return t.begin(); }
18     iterator end() { return t.end(); }
19     pair<iterator,bool> insert(const value_type& x) { return t.insert_unique(x); }
20      // ...
21 }
View Code

 

另外STL中有未列入標准的hash_set/hash_map以及C++11中的unordered_set/map,底層是使用hashtable實現的。

相比於用rb_tree實現的set/map,它們的插入刪除查找操作具有O(1)的時間復雜度(沒有沖突情況下),但是它們的元素的順序是無序的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM