散列表的開放尋址法

開放尋址法(open addressing)中,所有元素都存放在槽中,在鏈表法散列表中,每個槽中保存的是相應鏈表的指針,爲了維護一個鏈表,鏈表的每個結點必須有一個額外的域來保存它的前戲和後繼結點。開放尋址法不在槽外保存元素,不使用指針,也不必須爲了維護一個數據結構使用額外的域,所有可以不用存儲指針而節省的空間,使得可以用同樣的空間來提供更多的槽,也潛在地減少了衝突,提高了檢索速度。

爲了使用開放尋址法插入一個元素,需要連續地檢查散列表,或稱爲探查(probe),直到找到一個空槽來放置待插入的關鍵字爲止。

有三種常用技術來計算開放尋址法中的探查序列:線性探查、二將探查和雙重探查。

1.線性探查:給定個一個普通的散列函數 h':U->{0, 1, ..., m-1},稱之爲輔助散列函數,線性探查方法採用的散列函數爲:

h(k, i) = (h'(k) + 1) mode n,i = 0, 1, ...,m-1

給定一個一個關鍵字k,首先探查 T[h'(k)],即由輔助散列函數所給出的槽位,再探查槽 T[h'(k) + 1], 依次類推,直至槽 T[m - 1],然後,又繞到 T[0], T[1],...,直到最後探查到槽 T[h'(k) - 1],在線性探查方法中,初始探查位置決定了整個序列,故只有 m 種不同的探查序列。

2.二次探查:採用如下列式的散列函數:h(k, i) = (h'(k) + c1i + c2i^2) mod m

其中 h' 是一個輔助散列波函數,c1 和 c2 爲正的輔助常數,i = 0, 1, ... , m - 1。

象線性探查一樣,二次探查的初始探查位置決定了整個序列。

3.雙重散列:雙重散列是用於開放尋址法的最好方法之一,因爲它所產生的排列具有隨機選擇排列的這麼多特性。雙重散列採用以下形式的散列函數:h(k, i) = (h1(k) + ih2(k)) mod m

其中 h1 和 h2 均爲輔助散列函數。初始探查位置爲 T[h1(k)],後續的探查位置是前一位置加上偏移量 h2(k) 模 m。爲了能查找整個散列表,值 h2(k) 必須要與表的大小 m 互素。

以下是開放尋址法的一個類的定義的例子:

  1. #ifndef _OPEN_ADDRESSING_HASH_H_  
  2. #define _OPEN_ADDRESSING_HASH_H_  
  3.   
  4. /************************************************************************ 
  5.  算法導論 
  6.  
  7.  開放尋址法散列表,本全程採用雙重散列的散列函數,其中 h1(k) = k % m, 
  8.  h2(k) = 1 + k % (m - 1) 
  9.  ************************************************************************/  
  10.   
  11. #include <stdexcept>  
  12.   
  13. template <class T>  
  14. class OpenAddressingHash{  
  15. public:  
  16.     // 定義一個散列元素類型  
  17.     struct Node {  
  18.         friend class OpenAddressingHash < T > ;  
  19.         // 散列元素鍵值,key 必須 >= 0,當 key == -1 時,表示槽是空的,  
  20.         // 當 key == -2 時表示槽內元素已刪除  
  21.         int key;  
  22.         T value;  
  23.     private:  
  24.         Node() :key(-1){}  
  25.         Node(int k, const T& v) :key(k), value(v){}  
  26.     };  
  27.   
  28.     // 插入一個元素  
  29.     Node* insert(size_t key, const T& value);  
  30.   
  31.     // 查找一個元素  
  32.     Node* search(size_t key);  
  33.   
  34.     // 刪除一個散列元素  
  35.     void remove(size_t key);  
  36.   
  37. private:  
  38.     // 散列表大小  
  39.     static const size_t _table_size = 11;  
  40.     // 散列表  
  41.     Node _table[_table_size];  
  42.   
  43.     // 散列函數  
  44.     size_t hash(size_t k, size_t);  
  45.     // 輔助散列函數 h1 h2  
  46.     inline size_t hash1(size_t k);  
  47.     inline size_t hash2(size_t k);  
  48. };  
  49.   
  50. template <class T>  
  51. typename OpenAddressingHash<T>::Node* OpenAddressingHash<T>::insert(size_t key, const T& value){  
  52.     size_t i = 0;  
  53.     while (i != _table_size) {  
  54.         auto hashCode = hash(key, i);  
  55.         auto node = &_table[hashCode];  
  56.         // 如果槽中關鍵字與要插入的關鍵字相同,則修改元素的值  
  57.         if (node->key == key || node->key == -2 || node->key == -1){  
  58.             node->key = static_cast<int>(key);  
  59.             node->value = value;  
  60.             return node;  
  61.         }  
  62.         ++i;  
  63.     }  
  64.     throw std::overflow_error("hash table overflow");  
  65. }  
  66.   
  67.   
  68. template <class T>  
  69. typename OpenAddressingHash<T>::Node* OpenAddressingHash<T>::search(size_t key){  
  70.     size_t i = 0;  
  71.     while (i != _table_size)  
  72.     {  
  73.         auto hashCode = hash(key, i++);  
  74.         if (_table[hashCode].key == key)  
  75.             return &_table[hashCode];  
  76.     }  
  77.     return nullptr;  
  78. }  
  79.   
  80. template <class T>  
  81. void OpenAddressingHash<T>::remove(size_t key){  
  82.     auto node = search(key);  
  83.     if (node)  
  84.         // 將 key 設置爲 -2,表示當前槽元素已刪除  
  85.         // 不要將 key 設置爲 -1,如果這樣可導致之後具有相同散列值的元素不可訪問  
  86.         node->key = -2;  
  87. }  
  88.   
  89. template <class T>  
  90. size_t OpenAddressingHash<T>::hash(size_t key, size_t i){  
  91.     return (hash1(key) + i * hash2(key)) % _table_size;  
  92. }  
  93.   
  94. template <class T>  
  95. size_t OpenAddressingHash<T>::hash1(size_t key){  
  96.     return key % _table_size;  
  97. }  
  98.   
  99. template <class T>  
  100. size_t OpenAddressingHash<T>::hash2(size_t key){  
  101.     return key % (_table_size - 1) + 1;  
  102. }  
  103.   
  104. #endif 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章