哈希表(闭散列、拉链法--哈希桶)

哈希表,也称散列表,是一种通过key值来直接访问在内存中的存储的数据结构。它通过一个关键值的函数(被称为散列函数)将所需的数据映射到表中的位置来访问数据。

关于哈希表,主要为以下几个方面:

一、哈希表的几种方法

1、直接定址法:取关键字key的某个线性函数为散列地址,如Hash(key) = key  或 Hash(key) = A*key+B;A,B为常数

2、除留取余法:关键值除以比散列表长度小的素数所得的余数作为散列地址。Hash(key) = key % p;

3、平均取中法:先计算构成关键码的标识符的内码的平方,然后按照散列表的大小取中间的若干位作为散列地址。

4、折叠法:把关键码自左到右分为位数相等的几部分,每一部分的位数应与散列表地址位数相同,只有最后一部分的位数可以短一些。把这些部分的数据叠加起来,就可以得到具有关键码的记录的散列地址。分为移位法和分界法。

5、随机数法:选择一个随机函数,取关键字的随机函数作为它的哈希地址。

6、数学分析法:设有N个d位数,每一位可能有r种不同的符号。这r种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布均匀些,每种符号出现的机会均等;在某些位上分布不均匀,只有某几种符号经常出现。可根据散列表的大小,选取其中各种符号分布均匀的若干位作为散列地址。


在这里,我们建哈希表的方法用除留取余法为例。

尽管有这么多种方法,但是不同的key值可能会映射到同一散列地址上。这样就会造成哈希冲突/哈希碰撞


那么遇到哈希冲突我们该如何处理呢?


二、处理哈希冲突的闭散列方法

1、线性探测:当不同的key值通过哈希函数映射到同一散列地址上时,检测当前地址的下一个地址是否可以插入,如果可以的话,就存在当前位置的下一个地址,否则,继续向下一个地址寻找,地址++。

2、二次探测:是针对线性探测的一个改进,线性探测后插入的key值太集中,这样造成key值通过散列函数后还是无法正确的映射到地址上,太集中也会造成查找、删除时的效率低下。因此,通过二次探测的方法,取当前地址加上i^2,可以取到的新的地址就会稍微分散开。

如:此时的散列表长度为10:


看到以上的例子之后,我们只是存入int型的数据时,计算余数寻找地址是比较容易的。如果我们存入的是字典值(string类型),那么我们就要通过string类型来转化成数值来计算地址。这里用到了BKDR哈希算法(字符串哈希算法)。

同时,经研究表明,通过素数表作为哈希表的长度可以降低哈希冲突。


三、闭散列实现代码

主要实现如下:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma once  
  2. #include<vector>  
  3. #include<iostream>  
  4. using namespace std;  
  5. #include<assert.h>  
  6. #include<string>  
  7.   
  8. enum Status  
  9. {  
  10.     EXIST,  
  11.     DELETE,  
  12.     EMPTY  
  13. };  
  14.   
  15. template<class K,class V>  
  16. struct HashTableNode  
  17. {  
  18.     K _key;  
  19.     V _value;  
  20.     Status _status;  
  21.     HashTableNode(const K& key = K(), const V& value = V())  
  22.         :_key(key)  
  23.         ,_value(value)  
  24.         ,_status(EMPTY)  
  25.     {}  
  26. };  
  27.   
  28. template<class K>  
  29. struct __HashFunc  
  30. {  
  31.     size_t operator()(const K& key)  
  32.     {  
  33.         return key;  
  34.     }  
  35. };  
  36.   
  37. template<>  
  38. struct __HashFunc<string>  
  39. {  
  40.     size_t BKDRHash(const char* str)  
  41.     {  
  42.         register size_t hash = 0;  
  43.         while(*str)  
  44.         {  
  45.             hash = hash*131 + *str;  
  46.             ++str;  
  47.         }  
  48.         return hash;  
  49.     }  
  50.     size_t operator()(const string& str)  
  51.     {  
  52.         return BKDRHash(str.c_str());  
  53.     }  
  54. };  
  55. template<class K,class V,class _HashFunc = __HashFunc<K>>  
  56. class HashTable  
  57. {  
  58.     typedef HashTableNode<K,V> Node;  
  59. public:  
  60.     HashTable(size_t size)  
  61.         :_size(0)  
  62.     {  
  63.         assert(size > 0);  
  64.     //  _tables.resize(size);  
  65.         _tables.resize(GetPrime());  
  66.       
  67.     }  
  68.   
  69.     ~HashTable()  
  70.     {}  
  71.     size_t HashFunc(const K& key)  
  72.     {  
  73.         _HashFunc hf;  
  74.         size_t va = hf(key);  
  75.         return va % _tables.size();  
  76.     }  
  77.   
  78.     void Swap(HashTable<K,V,_HashFunc>& ht)  
  79.     {  
  80.         _tables.swap(ht._tables);  
  81.         swap(_size,ht._size);  
  82.     }  
  83.   
  84.     void _CheckCapacity()  
  85.     {  
  86.         if(_tables.size() == 0 || _size*10 / _tables.size() >= 7)  
  87.         {  
  88.             size_t OldSize = _tables.size();  
  89.         //  size_t NewSize = _tables.size()*2+3;  
  90.             size_t NewSize = GetPrime();  
  91.             HashTable<K,V,_HashFunc> ht(NewSize);  
  92.             for(size_t i = 0; i < OldSize; i++)  
  93.             {  
  94.                 if(_tables[i]._status == EXIST)  
  95.                 {  
  96.                     ht.Insert(_tables[i]._key,_tables[i]._value);  
  97.                 }  
  98.             }  
  99.             this->Swap(ht);  
  100.         }  
  101.     }  
  102.   
  103.     pair<Node*,bool> Insert(const K& key,const V& value)  
  104.     {  
  105.         _CheckCapacity();  
  106.         size_t index = HashFunc(key);  
  107.         //线性探测  
  108.         /*while(_tables[index]._status ==  EXIST ) 
  109.         { 
  110.             if(_tables[index]._key == key) 
  111.                 return make_pair((Node*)NULL,false); 
  112.             ++index; 
  113.  
  114.             if(index == _tables.size()) 
  115.             { 
  116.                 index = 0; 
  117.             } 
  118.         } 
  119.         */  
  120.         //二次探测  
  121.         size_t i = 0;  
  122.         size_t first = index;  
  123.         while(_tables[index]._status ==  EXIST )  
  124.         {  
  125.             if(_tables[index]._key == key)  
  126.                 return make_pair((Node*)NULL,false);  
  127.             ++i;  
  128.             index = first + i*i;  
  129.             index %= _tables.size();  
  130.         }  
  131.         ++_size;  
  132.         _tables[index]._key = key;  
  133.         _tables[index]._value = value;  
  134.         _tables[index]._status = EXIST;  
  135.         return make_pair(&_tables[index],true);  
  136.     }  
  137.   
  138.     Node* Find(const K& key,const V& value)  
  139.     {  
  140.         size_t index = HashFunc(key);  
  141.         while(_tables[index]._status != EMPTY)  
  142.         {  
  143.             if(_tables[index]._key == key && _tables[index]._status == EXIST)  
  144.             {  
  145.                 return &_tables[index];  
  146.             }  
  147.             else  
  148.             {  
  149.                 ++index;  
  150.                 if(index == _tables.size())  
  151.                 {  
  152.                     index = 0;  
  153.                 }  
  154.             }  
  155.         }  
  156.         return NULL;  
  157.     }  
  158.   
  159.     bool Remove(const K& key,const V& value)  
  160.     {  
  161.         Node* tmp = Find(key,value);  
  162.         if(tmp)  
  163.         {  
  164.             tmp->_status = DELETE;  
  165.             return true;  
  166.         }  
  167.         return false;  
  168.     }  
  169.     size_t GetPrime()  
  170.     {  
  171.         // 使用素数表对齐做哈希表的容量,降低哈希冲突  
  172.         const int _PrimeSize = 28;  
  173.         static const unsigned long _PrimeList[_PrimeSize] =  
  174.         {  
  175.             53ul, 97ul, 193ul, 389ul, 769ul,  
  176.             1543ul, 3079ul, 6151ul, 12289ul,   
  177.             24593ul,49157ul, 98317ul, 196613ul,   
  178.             393241ul,786433ul,1572869ul, 3145739ul,  
  179.             6291469ul, 12582917ul,25165843ul,50331653ul,   
  180.             100663319ul, 201326611ul, 402653189ul,  
  181.             805306457ul,1610612741ul, 3221225473ul, 4294967291ul  
  182.         };  
  183.         for(size_t i = 0; i < _PrimeSize; i++)  
  184.         {  
  185.             if(_tables.size() < _PrimeList[i])  
  186.                 return _PrimeList[i];  
  187.         }  
  188.         return 0;  
  189.     }  
  190. private:  
  191.     vector<Node> _tables;  
  192.     size_t _size;  
  193. };  
  194.   
  195. void HashTest()  
  196. {  
  197.     HashTable<int,int> ht(10);  
  198.     ht.Insert(89,0);  
  199.     ht.Insert(18,0);  
  200.     ht.Insert(49,0);  
  201.     ht.Insert(58,0);  
  202.     ht.Insert(9,0);  
  203.       
  204.     cout<<ht.Remove(58,0)<<endl;  
  205.     if(ht.Find(49,0))  
  206.         cout<<ht.Find(9,0)->_key<<endl;  
  207.   
  208.     HashTable<string,string> ht1(10);  
  209.     ht1.Insert("sort","排序");  
  210.     ht1.Insert("left","左边");  
  211.     ht1.Insert("right","右边");  
  212.     ht1.Insert("up","上边");  
  213.     if(ht1.Find("sort","排序"))  
  214.         cout<<ht1.Find("sort","排序")->_key<<endl;  
  215.     cout<<ht1.Remove("sort","排序")<<endl;  
  216. }  


四、处理哈希冲突的开链法(哈希桶)

当用线性探测和二次探测时,总是在一个有限的哈希表中存储数据,当数据特别多时,效率就比较低。因此采用拉链法的方式来降低哈希冲突。



还有一种情况是,当一个链上链的数据过多时,我们可以采用红黑树的方式来降低高度,保持平衡且不至于过载。


五、哈希桶的实现方式(考虑到存储整形和字符串型)

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #pragma once  
  2. #include<iostream>  
  3. using namespace std;  
  4. #include<vector>  
  5. #include<string>  
  6.   
  7. namespace HashBucket  
  8. {  
  9.     template<class K>  
  10.     struct __HashFunc  
  11.     {  
  12.         size_t operator()(const K& key)  
  13.         {  
  14.             return key;  
  15.         }  
  16.     };  
  17.   
  18.     template<>  
  19.     struct __HashFunc<string>  
  20.     {  
  21.         size_t BKDRHash(const char* str)  
  22.         {  
  23.             register size_t hash = 0;  
  24.             while(*str)  
  25.             {  
  26.                 hash = hash*131 + *str;  
  27.                 ++str;  
  28.             }  
  29.             return hash;  
  30.         }  
  31.         size_t operator()(const string& str)  
  32.         {  
  33.             return BKDRHash(str.c_str());  
  34.         }  
  35.     };  
  36.   
  37.     template<class K,class V,class _HashFunc>  
  38.     class HashTable;  
  39.   
  40.     template<class K,class V>  
  41.     struct HashNode  
  42.     {  
  43.         pair<K,V> _kv;  
  44.         HashNode<K,V> *_next;  
  45.   
  46.         HashNode(const pair<K,V>& kv)  
  47.             :_kv(kv)  
  48.             ,_next(NULL)  
  49.         {}  
  50.     };  
  51.     template<class K,class V,class Ref,class Ptr>  
  52.     struct HashIterator  
  53.     {  
  54.         typedef HashNode<K,V> Node;  
  55.         typedef HashIterator<K,V,Ref,Ptr> Self;  
  56.         Node* _node;  
  57.         HashTable<K,V,__HashFunc<K>>* _ht;  
  58.   
  59.         HashIterator(Node* node,HashTable<K,V,__HashFunc<K>>* ht)  
  60.             :_node(node)  
  61.             ,_ht(ht)  
  62.         {}  
  63.   
  64.         Ref operator*()  
  65.         {  
  66.             return _node->_kv;  
  67.         }  
  68.   
  69.         Ptr operator->()  
  70.         {  
  71.             return &_node;  
  72.         }  
  73.   
  74.         bool operator!=(const Self& s) const  
  75.         {  
  76.             return _node != s._node;  
  77.         }  
  78.   
  79.         Self& operator++()  
  80.         {  
  81.             _node = Next(_node);  
  82.             return *this;  
  83.         }  
  84.   
  85.         Node* Next(Node* node)  
  86.         {  
  87.             Node* next = node->_next;  
  88.             if(next)  
  89.                 return next;  
  90.             else  
  91.             {  
  92.                 size_t index = _ht->HashFunc(node->_kv.first)+1;  
  93.                 for(;index < _ht->_tables.size();++index)  
  94.                 {  
  95.                     next = _ht->_tables[index];  
  96.                     if(next)  
  97.                     {  
  98.                         return next;  
  99.                     }  
  100.                 }  
  101.                 return NULL;  
  102.             }  
  103.         }  
  104.     };  
  105.   
  106.     template<class K,class V,class _HashFunc = __HashFunc<K>>  
  107.     class HashTable  
  108.     {  
  109.         typedef HashNode<K,V> Node;  
  110.     public:  
  111.         typedef HashIterator<K,V,pair<K,V>&,pair<K,V>*> Iterator;  
  112.         typedef HashIterator<K,V,const pair<K,V>&,const pair<K,V>*> ConstIterator;  
  113.         friend struct Iterator;  
  114.         friend struct ConstIterator;  
  115.     public:  
  116.         HashTable()  
  117.         :_size(0)  
  118.         {  
  119.             _tables.resize(GetNextPrime());  
  120.         }  
  121.   
  122.         ~HashTable()  
  123.         {  
  124.             Clear();  
  125.         }  
  126.   
  127.         void Clear()  
  128.         {  
  129.             Node* cur = NULL;  
  130.             Node* del = NULL;  
  131.             for(size_t index = 0; index < _tables.size(); ++index)  
  132.             {  
  133.                 cur = _tables[index];  
  134.                 if(cur == NULL)  
  135.                 {  
  136.                     continue;  
  137.                 }  
  138.                 while(cur)  
  139.                 {  
  140.                     del = cur;  
  141.                     cur = cur->_next;  
  142.                     delete del;  
  143.                     del = NULL;  
  144.                 }  
  145.             }  
  146.   
  147.         }  
  148.   
  149.         Iterator Begin()  
  150.         {  
  151.             Node* cur = _tables[0];  
  152.             for(size_t index = 0; index < _tables.size();++index)  
  153.             {  
  154.                 cur = _tables[index];  
  155.                 if(cur)  
  156.                 {  
  157.                     return Iterator(cur,this);  
  158.                 }  
  159.             }  
  160.             return Iterator((Node*)NULL,this);  
  161.         }  
  162.   
  163.         Iterator End()  
  164.         {  
  165.             return Iterator((Node*)NULL,this);  
  166.             /*Node* cur = _tables[_tables.size()-1]; 
  167.             while(cur) 
  168.             { 
  169.                 if(cur == NULL) 
  170.                 { 
  171.                     return Iterator(cur,this); 
  172.                 } 
  173.                 cur = cur->_next; 
  174.             } 
  175.             return Iterator((Node*)NULL,this);*/  
  176.         }  
  177.   
  178.         size_t HashFunc(const K& key)  
  179.         {  
  180.             _HashFunc hf;  
  181.             size_t va = hf(key);  
  182.             return va % _tables.size();  
  183.         }  
  184.   
  185.         void Swap(HashTable<K,V,_HashFunc>& ht)  
  186.         {  
  187.             _tables.swap(ht._tables);  
  188.             swap(_size,ht._size);  
  189.         }  
  190.   
  191.         void _CheckCapacity()  
  192.         {  
  193.             //负载因子为1时,扩容  
  194.             if(_size == _tables.size())  
  195.             {  
  196.                 size_t index = GetNextPrime();  
  197.                 HashTable<K,V> tmp;  
  198.                 tmp._tables.resize(index);  
  199.                 Node* cur = NULL;  
  200.                 for(;index < _tables.size();++index)  
  201.                 {  
  202.                     cur = _tables[index];  
  203.                     while(cur)  
  204.                     {  
  205.                         tmp.Insert(cur->_kv);  
  206.                         cur = cur->_next;  
  207.                     }  
  208.                 }  
  209.                 this->Swap(tmp);  
  210.             }  
  211.         }  
  212.   
  213.         size_t GetNextPrime()  
  214.         {  
  215.             // 使用素数表对齐做哈希表的容量,降低哈希冲突  
  216.             const int _PrimeSize = 28;   
  217.             static const unsigned long _PrimeList[_PrimeSize] =  
  218.             {  
  219.                 53ul, 97ul, 193ul, 389ul, 769ul,  
  220.                 1543ul, 3079ul, 6151ul, 12289ul,   
  221.                 24593ul,49157ul, 98317ul, 196613ul,   
  222.                 393241ul,786433ul,1572869ul, 3145739ul,  
  223.                 6291469ul, 12582917ul,25165843ul,50331653ul,   
  224.                 100663319ul, 201326611ul, 402653189ul,  
  225.                 805306457ul,1610612741ul, 3221225473ul, 4294967291ul  
  226.             };  
  227.             for(size_t i = 0; i < _PrimeSize; i++)  
  228.             {  
  229.                 if(_tables.size() < _PrimeList[i])  
  230.                     return _PrimeList[i];  
  231.             }  
  232.             return 0;  
  233.         }  
  234.     public:  
  235.         pair<Iterator,bool> Insert(pair<K,V> kv)  
  236.         {  
  237.             _CheckCapacity();  
  238.             size_t index = HashFunc(kv.first);  
  239.             Node* cur = _tables[index];  
  240.             while(cur)  
  241.             {  
  242.                 if(cur->_kv.first == kv.first)  
  243.                 {  
  244.                     return make_pair(Iterator(cur,this),false);  
  245.                 }  
  246.                 cur = cur->_next;  
  247.             }  
  248.             Node* tmp = new Node(kv);  
  249.             tmp->_next = _tables[index];  
  250.             _tables[index] = tmp;  
  251.             _size++;  
  252.             return make_pair(Iterator(tmp,this),true);  
  253.         }  
  254.   
  255.         Node* Find(const K& key)  
  256.         {  
  257.             size_t index = HashFunc(key);  
  258.             Node* cur = _tables[index];  
  259.             while(cur)  
  260.             {  
  261.                 if(cur->_kv.first == key)  
  262.                 {  
  263.                     return cur;  
  264.                 }  
  265.                 cur = cur->_next;  
  266.             }  
  267.             return NULL;  
  268.         }  
  269.   
  270.         bool Erase(const K& key)  
  271.         {  
  272.             size_t index = HashFunc(key);  
  273.             Node* prev = NULL;  
  274.             Node* cur = _tables[index];  
  275.             Node* del = NULL;  
  276.             while (cur)  
  277.             {  
  278.                 if(cur->_kv.first == key)  
  279.                 {  
  280.                     if(prev == NULL)  
  281.                     {  
  282.                         _tables[index] = cur->_next;  
  283.                     }  
  284.                     else  
  285.                     {  
  286.                         prev->_next = cur->_next;  
  287.                     }  
  288.                     delete cur;  
  289.                     cur = NULL;  
  290.                     _size--;  
  291.                     return true;  
  292.                 }  
  293.                 prev = cur;  
  294.                 cur = cur->_next;  
  295.             }  
  296.             return false;  
  297.         }  
  298.     private:  
  299.         vector<Node*> _tables;  
  300.         size_t _size;  
  301.     };  
  302.   
  303.     void HashTest()  
  304.     {  
  305.         HashTable<int,int> ht;  
  306.         ht.Insert(make_pair<int,int>(89,0));  
  307.         ht.Insert(make_pair<int,int>(18,0));  
  308.         ht.Insert(make_pair<int,int>(49,0));  
  309.         ht.Insert(make_pair<int,int>(58,0));  
  310.         ht.Insert(make_pair<int,int>(9,0));  
  311.         cout<<ht.Erase(58)<<endl;  
  312.         if(ht.Find(49))  
  313.             cout<<ht.Find(9)->_kv.first<<endl;  
  314.         HashTable<int,int>::Iterator it = ht.Begin();  
  315.         while(it != ht.End())  
  316.         {  
  317.             cout<<(*it).first<<":"<<(*it).second<<endl;  
  318.             ++it;  
  319.         }  
  320.   
  321.         HashTable<string,string> ht1;  
  322.         ht1.Insert(make_pair<string,string>("sort","排序"));  
  323.         ht1.Insert(make_pair<string,string>("left","左边"));  
  324.         ht1.Insert(make_pair<string,string>("right","右边"));  
  325.         ht1.Insert(make_pair<string,string>("up","上边"));  
  326.   
  327.         cout<<ht1.Erase("up")<<endl;  
  328.         cout<<ht1.Find("sort")->_kv.second<<endl;  
  329.         HashTable<string,string>::Iterator it1 = ht1.Begin();  
  330.         while(it1 != ht1.End())  
  331.         {  
  332.             cout<<(*it1).first<<":"<<(*it1).second<<endl;  
  333.             ++it1;  
  334.         }  
  335.     }  
  336. };  

哈希表(闭散列、拉链法--哈希桶)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值