数据结构学习——浅谈哈希表开散列和闭散列
生活随笔
收集整理的這篇文章主要介紹了
数据结构学习——浅谈哈希表开散列和闭散列
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
寫在前面
???????順序結構以及平衡樹中,元素關鍵碼與其存儲位置之間沒有對應的關系,因此在查找一個元素時,必須要經過關鍵碼的多次比較。順序查找時間復雜度為O(N),平衡樹中為樹的高度,即O( ),搜索的效率取決于搜索過程中元素的比較次數。
???????理想的搜索方法:可以不經過任何比較,一次直接從表中得到要搜索的元素。 如果構造一種存儲結構,通過某種函數(hashFunc)使元素的存儲位置與它的關鍵碼之間能夠建立一一映射的關系,那么在查找時通過該函數可以很快找到該元素。
當向該結構中:
插入元素
搜索元素
對元素的關鍵碼進行同樣的計算,把求得的函數值當做元素的存儲位置,在結構中按此位置取元素比較,若關鍵碼相等,則搜索成功該方式即為哈希(散列)方法,哈希方法中使用的轉換函數稱為哈希(散列)函數,構造出來的結構稱為哈希表(Hash Table)(或者稱散列表)
哈希沖突
對于兩個數據元素的關鍵字 和 (i != j),有 != ,但有:Hash(i ) == Hash(j ), 即:不同關鍵字通過相同哈希哈數計算出相同的哈希地址,該種現象稱為哈希沖突或哈希碰撞.把具有不同關鍵碼而具有相同哈希地址的數據元素稱為“同義詞”。
發生哈希沖突該如何處理呢?
哈希沖突解決
解決哈希沖突兩種常見的方法是:閉散列和開散列1.閉散列的實現
閉散列:也叫開放定址法,當發生哈希沖突時,如果哈希表未被裝滿,說明在哈希表中必然還有空位置, 那么可以把key存放到沖突位置中的“下一個” 空位置中去。那如何尋找下一個空位置呢?類的成員變量:
private:vector<elem> _ht;int _size; /enum state{empty,exist,deleted};typedef struct elem{pair<K, V> val;state sta;}elem; //1.閉散列 #include<map> #include<vector> #include<utility> #include<iostream> using namespace std;const int PRIMECOUNT = 28; const size_t primeList[PRIMECOUNT] = {53ul, 97ul, 193ul, 389ul, 769ul,1543ul, 3079ul, 6151ul, 12289ul, 24593ul,49157ul, 98317ul, 196613ul, 393241ul, 786433ul,1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,1610612741ul, 3221225473ul, 4294967291ul };enum state{empty,exist,deleted};template<class K,class V>class hash_1{typedef struct elem{pair<K, V> val;state sta;}elem;public:hash_1(int n = 3):_size(0), _ht(n){for (int i = 0; i < _ht.capacity(); ++i){_ht[i].sta = empty;}}bool insert(const K& key){check_capacity();size_t _hashaddr = hash_func(key);pair<K, V>_val = { key, _hashaddr }; //pair類型的變量初始化size_t _start = _hashaddr;while (_ht[_hashaddr].sta == exist){if (_ht[_hashaddr].sta == exist && _ht[_hashaddr].val.first == key)return false;_hashaddr++;if (_hashaddr == _ht.capacity())_hashaddr == 0;if (_hashaddr == _start)return false;}//如果為空,可以直接插入_ht[_hashaddr].val = _val;_ht[_hashaddr].sta = exist;++_size;return true;}void check_capacity(){//增容的條件是: α>=0.7if (_size*10/_ht.capacity()>=7){hash_1<K, V>newht(getnextprime(_ht.capacity()));for (int i = 0; i < _ht.capacity(); ++i){if (_ht[i].sta == exist){newht.insert(_ht[i].val.first);}}Swap(newht);}}int getnextprime(size_t n){for (int i = 0; i < PRIMECOUNT; ++i){if (primeList[i]>n)return primeList[i];}return 0;}int find(const K& key){//如果找到了,返回下標;沒有找到就打印“不存在”;int _hashaddr = hash_func(key);size_t start = _hashaddr;while ( _ht[_hashaddr].sta != empty ){if (_ht[_hashaddr].sta == exist && _ht[_hashaddr].val.first == key)return _hashaddr;++_hashaddr;if (_hashaddr == _ht.capacity()){_hashaddr = 0;}if (_hashaddr == start){cout << "不存在" << endl;return -1;}}cout << "不存在" << endl;return -1;}void erase(const K& key){int index = find(key);if (index != -1){_ht[index].sta = deleted;++_size;}return;}void Swap(hash_1<K, V>& ht){swap(_ht, ht._ht);swap(_size, ht._size);}private: size_t hash_func(const K& key){return key % _ht.capacity();}private:vector<elem> _ht;int _size;};2.開散列的實現
開散列法又叫鏈地址法(開鏈法),首先對關鍵碼集合用散列函數計算散列地址, 具有相同地址的關鍵碼歸于同一子集合,每一個子集合稱為一個桶,各個桶中的元素通過一個單鏈表鏈接起來, 各鏈表的頭結點存儲在哈希表中。
類的成員變量:
開散列與比散列比較:
應用鏈地址法處理溢出,需要增設鏈接指針,似乎增加了存儲開銷。事實上: 由于開地址法必須保持大量的空閑空間以確保搜索效率,如二次探查法要求裝載因子a <= 0.7,而表項所占空間又比指針大的多,所以使用鏈地址法反而比開地址法節省存儲空間。
總結
以上是生活随笔為你收集整理的数据结构学习——浅谈哈希表开散列和闭散列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 docker 来安装 oracle
- 下一篇: 超火的ipad procreate必备神