STL源碼剖析(二)

容器rb_tree

Red-Black tree(紅黑樹)是平衡二叉搜索樹(balanced binary search tree)中常被使用的一種。平衡二叉搜索樹的特色:排列規則有禮 search 和 insert,並保持高度平衡—————無任何節點過深。node

rb_tree提供「便利」操做及iterators。按正常規則(++ite)遍歷,便能得到排序狀態(sorted)。編程

由於rb_tree有必定的排序規則,因此咱們不該該使用rb_tree的iterator改變元素值 編程層面並未禁止此事。 這樣設計的緣由是:rb_tree是set和map的底層實現依託,而map運行元素的 data 被改變,只有元素的 key 纔是不可改變的。數組

rb_tree提供兩種insertion操做:insert_unuque()和insert_equal()。前者表示節點的key必定能夠在整個tree中獨一無二,不然插入失敗;後者表示節點的key能夠重複。app

template <class Key,
          class Value,
          class KeyOfValue,
          class Compare,
          class Alloc = alloc>
class te_tree{
protected:
    typedef __rb_tree_node<Value> rb_tree_node;
    ...
public:
    typedef rb_tree_node* link_type;
    ...
protected:
    size_type node_count;   //記錄rb_tree的節點數量
    link_type header;       //頭指針
    Compare key_compare;    //key的大小比較規則;應該是個function object
    ...
}

模板中Key+value一塊兒合成爲value,KeyOfValue就是告訴模板如何肯定value中的key是什麼。less

set/multiset

set/multiset是以rb_tree爲底層結構,所以有 元素自動排序 的特性。排序是依據 key 進行的,而set/multiset元素的 value和key合一:value就是key。 同時,set/multiset提供遍歷操做和迭代器。按照正常的++ite遍歷,便能得到排序狀態後的序列。ide

咱們沒法使用set/multiset的迭代器改變元素的值,會打亂rb_tree中的順序。因此set/multiset使用的迭代器就是rb_tree的 const iterator ,目的就是禁止用戶對元素進行賦值。函數

set元素的key必須獨一無二,所以insert()用的是rb_tree的 insert_unique() ;multiset元素的key能夠重複,所以insert()用的是rb_tree的 insert_equal()設計

set

template <class Key,
          class Compare = less<Key>,
          class Alloc = alloc>
class set {
    typedef Key key_type;
    typedef Key value_type;
    typedef Compare key_compare;
    typedef Compare value_compare;
private:
    typedef rb_tree<key_type,
                    value_type,
                    identity<value_type>,//VC6中沒有identity,提供了一個與identity類似的操做
                    key_compare,
                    Alloc> rep_type;
    rep_type t;
public:
    typedef typename rep_type::const_iterator iterator;

...
};

set的全部操做,都轉交給底層的t進行操做,因此能夠將set看做一個容器適配器(container adapter)。指針

map/multimap

map/multimap以rb_tree爲底層結構,所以有 元素自動排序 的特性。同時,set/multiset提供遍歷操做和迭代器。按照正常的++ite遍歷,便能得到排序狀態後的序列。code

咱們沒法使用map/multimap的迭代器改變元素的key(由於key有其謹慎的排序規則),可是能夠用它來改變元素的data。因map/multimap內部將用戶指定的 key type 設置爲const, 以便能禁止用戶對元素的key賦值。

map元素的key必須獨一無二,所以insert()用的是rb_tree的 insert_unique() ;multimap元素的key能夠重複,所以insert()用的是rb_tree的 insert_equal()

map

template <class Key,
          class T,
          class Compare = less<Key>,
          class Alloc=alloc>
class map {
public:
    typedef Key key_type;
    typedef T data_type;
    typedef T mapped_type;
    typedef pair<const Key, T> value_type;
    typedef Compare key_compare;
private
    typedef rb_tree<key_type, 
                    value_type, 
                    select1st<value_type>, //vc6中一樣沒有此函數,但提供了類似的功能
                    key_compare, 
                    Alloc> rep_type;
    rep_type t;
public:
    typedef typename rep_type::iterator iterator;

...
};

map獨有的operator[]

map的[]操做符號用來返回指定key所對應的data, 若是key不存在,則會建立一個帶有此key的元素放入map。

容器hashtable

hashtable做爲unordered_set和unordered_map的底層結構。hashtable主要的思想就是將給定的元素經過哈希函數計算後獲得一個哈希碼(Hash Code),這個給定的元素就能夠看做爲value, 而生成的哈希碼能夠看爲key。有如下的結論:

  • 若是兩個元素相等,那麼經過相同的函數計算獲得的哈希碼必定相同
  • 若是兩個元素的哈希碼相同,並不能說明兩個元素是相同的,只能說明兩個元素在三列存儲結構中,存放於同一個位置。

以int爲元素類型,一個hashtable就是用來表現hashcode和元素之間的映射關係,能夠定義一個必定大小的數組用來保存這種映射關係。hashtable中的每個位置稱爲一個bucket。假設hashtable的大小爲53,如今有些須要存放的數據{59, 63, 108, 2, 53, 55}。咱們能夠用一個簡單的哈希函數來計算哈希碼:取餘法

static int indexFor(int h, int length) {  
    return h % length;
}

這樣59就存放在序號爲6的位置;63就存放在序號爲10的位置;108就在序號爲2的位置上;2就在序號爲2的位置上;53就存放在序號爲0的位置;55就存放在序號爲2的位置。

經過這樣的方式能夠快速訪問到相應的元素,但也會發現108,2,55三個數的哈希碼衝突了,產生哈希碼的過程當中不免會產生衝突,一種好的衝突解決方案決定了hashtable的效率。目前經常使用的一種衝突解決方案是Separate Chaining。

這樣hashtable中每個bucket都存放一個鏈表,全部衝突的哈希碼都放在這個鏈表中。雖然說鏈表的查找是一個線型過程,可是當鏈表不長時查找的效率仍是不差的,當鏈表過長時,能夠將鏈表轉換爲紅黑樹提升查找效率。當整個hashtable中存放的數據過多,會由於鏈表過長致使查找效率急速降低。可是目前這個界限沒有明確的界定,根據你們的經驗來講,當hashtable中的數據個數大於bucket的個數時,效率就會受到影響。這個時候就須要對hashtable的bucket進行擴充,這個擴充過程通常來講是將bucket的數量乘以2,而後選出這個2倍數附近的一個 素數 。對於這樣固定模式的擴充,能夠不用在使用時進行計算,而是在程序中將須要擴充的數字寫好,在使用時直接調用。

static const unsigned long __stl_prime_list[__stl_num_primes] = {
    53,         97,           193,         389,       769,
    1543,       3079,         6151,        12289,     24593,
    49157,      98317,        196613,      393241,    786433,
    1572869,    3145739,      6291469,     12582917,  25165843,
    50331653,   100663319,    201326611,   402653189, 805306457, 
    1610612741, 3221225473ul, 4294967291ul
};

在擴充以後,由於bucket的個數發生了變化因此須要對整個hashtable進行從新計算。

template <class Value,
          class Key,
          class HashFcn,
          class ExtractKey,
          class EqualKey,
          class Alloc=alloc>
class hashtable {
public:
    typedef HashFcn hasher;
    typedef EqualKey key_equal;
    typedef size_t size_type;

private:
    hasher hash;
    key_equal equals;
    ExtractKey get_key;

    typedef __hashtable_node<Value> node;//單向鏈表

    vector<node*, Alloc> buckets;
    size_type num_elements;
public:
    size_type bucket_count() const { return buckets.size();}
...
}
相關文章
相關標籤/搜索