STL原始碼分析之hashtable關聯容器 上
阿新 • • 發佈:2018-12-08
前言
前面我們分析過RB-tree關聯容器, RB-tree
在插入(可重複和不可重複), 刪除等操作時間複雜度都是O(nlngn), 以及滿足5個規則, 以及以他為底層的配接器; 本節就來分析hashtable
另個關聯容器, 他在插入, 刪除等操作都可以做到O(1)的時間複雜度.
雜湊表概念
雜湊方法
- 直接定址法:取關鍵字或關鍵字的某個線性函式值為雜湊地址。(這種雜湊函式叫做自身函式)
- 數字分析法:假設關鍵字是以r為基的數,並且雜湊表中可能出現的關鍵字都是事先知道的,則可取關鍵字的若干數位組成雜湊地址。
- 平方取中法:取關鍵字平方後的中間幾位為雜湊地址。通常在選定雜湊函式時不一定能知道關鍵字的全部情況,取其中的哪幾位也不一定合適,而一個數平方後的中間幾位數和數的每一位都相關,由此使隨機分佈的關鍵字得到的雜湊地址也是隨機的。取的位數由表長決定。
- 摺疊法:將關鍵字分割成位數相同的幾部分(最後一部分的位數可以不同),然後取這幾部分的疊加和(捨去進位)作為雜湊地址。
- 隨機數法
6.除留餘數法:取關鍵字被某個不大於散列表表長m的數p除後所得的餘數為雜湊地址。不僅可以對關鍵字直接取模,也可在摺疊法、平方取中法等運算之後取模。對p的選擇很重要,一般取素數或m,若p選擇不好,容易產生衝突。
hashtable解決衝突的辦法就是開鏈
衝突處理
雜湊表的衝突處理也有很多種.
- 開放定址法
- 線性探測 : 本來的位置被佔有(衝突), 重新再往後找到第一個有空的位置插入進去
- 二次探測 : 本來的位置被佔有(衝突), 每次有衝突就平方一次重新查詢
- 開鏈 : 本來的位置被佔有(衝突), 形成一個連結串列插入到連結串列中
裝載因子 : 裝入表中的元素 / 表的實際大小. 裝載因子越大說明衝突的可能性就越大.
hashtable分析
桶與節點.
桶 : 定義的雜湊表大小, 以vector為桶
節點 : 連結串列
// 這裡連結串列是自定義的, 並沒有採用list和slist
template <class Value>
struct __hashtable_node
{
__hashtable_node* next;
Value val;
};
// 前置宣告
template < class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc = alloc>
class hashtable;
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator;
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_const_iterator;
hashtable迭代器
hashtable迭代器是forward_iterator_tag
型別, 正向迭代器, 所以他也就沒有過載--
, 沒有回退.
__hashtable_const_iterator
與__hashtable_iterator
一樣, 這裡就只分析後者
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator {
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> hashtable;
typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
const_iterator;
typedef __hashtable_node<Value> node;
typedef forward_iterator_tag iterator_category; // 正向迭代器
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef Value& reference;
typedef Value* pointer;
node* cur; // 定義節點
hashtable* ht; // 定義雜湊表指標
__hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
__hashtable_iterator() {}
// 過載指標
reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
// 重在++, 因為是正向迭代器, 所以沒有--
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const { return cur == it.cur; }
bool operator!=(const iterator& it) const { return cur != it.cur; }
};
定義雜湊表大小
定義了雜湊表的大小, 預設long為32位, 定義了28個數組大小. 雜湊表的的大小都是素數, 減少衝突
// Note: assumes long is at least 32 bits.
static const int __stl_num_primes = 28;
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, 3221225473, 4294967291
};
// 找到大於n最近的素數
inline unsigned long __stl_next_prime(unsigned long n)
{
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
雜湊表
hashtable型別定義
模板引數含義 :
- Value : 節點的實值型別
- Key : 節點的鍵值型別
- HashFcn : hash function的型別
- ExtractKey : 從節點中取出鍵值的方法(函式或仿函式)
- EqualKey : 判斷鍵值是否相同的方法(函式或仿函式)
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable {
public:
typedef Key key_type;
typedef Value value_type;
typedef HashFcn hasher;
typedef EqualKey key_equal;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
// 這裡返回的都是仿函式
hasher hash_funct() const { return hash; }
key_equal key_eq() const { return equals; }
private:
// 這裡定義的都是函式或者仿函式
hasher hash;
key_equal equals;
ExtractKey get_key;
typedef __hashtable_node<Value> node;
typedef simple_alloc<node, Alloc> node_allocator;
vector<node*,Alloc> buckets; // 以vector作為桶, node*
size_type num_elements; // 雜湊表中元素個數的計數
public:
typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> const_iterator;
// 迭代器定義為友元
friend struct
__hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
friend struct
__hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
...
};
構造與解構函式
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable {
...
public:
// 建構函式, 沒有定義預設建構函式
hashtable(size_type n, const HashFcn& hf,const EqualKey& eql,const ExtractKey& ext)
: hash(hf), equals(eql), get_key(ext), num_elements(0)
{
initialize_buckets(n);
}
hashtable(size_type n, const HashFcn& hf, const EqualKey& eql)
: hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0)
{
initialize_buckets(n);
}
// 拷貝建構函式
hashtable(const hashtable& ht)
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_elements(0)
{
copy_from(ht);
}
// 解構函式
~hashtable() { clear(); }
...
};
基本屬性獲取
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable {
...
public:
size_type size() const { return num_elements; }
size_type max_size() const { return size_type(-1); }
bool empty() const { return size() == 0; }
// 交換, 並不是交換所有資料, 只是交換了其指標指向和個數
void swap(hashtable& ht)
{
__STD::swap(hash, ht.hash);
__STD::swap(equals, ht.equals);
__STD::swap(get_key, ht.get_key);
buckets.swap(ht.buckets);
__STD::swap(num_elements, ht.num_elements);
}
iterator begin()
{
for (size_type n = 0; n < buckets.size(); ++n)
// 從頭遍歷桶, 如果有不空的連結串列存在, 就返回該連結串列的第一個元素
if (buckets[n])
return iterator(buckets[n], this);
// 沒有元素就返回end.
return end();
}
// end返回0
iterator end() { return iterator(0, this); }
const_iterator begin() const
{
for (size_type n = 0; n < buckets.size(); ++n)
if (buckets[n])
return const_iterator(buckets[n], this);
return end();
}
const_iterator end() const { return const_iterator(0, this); }
// 返回桶的大小
size_type bucket_count() const { return buckets.size(); }
size_type max_bucket_count() const
{ return __stl_prime_list[__stl_num_primes - 1]; }
// 返回指定位置的節點的個數
size_type elems_in_bucket(size_type bucket) const
{
size_type result = 0;
for (node* cur = buckets[bucket]; cur; cur = cur->next)
result += 1;
return result;
}
...
};
總結
本節只是分析了雜湊表的基本構成是桶(vector), 連結串列(解決衝突). hashtable
是forward_iterator_tag
型別的正向迭代器,沒有--
操作, 下節我們繼續分析剩下的程式碼.