当前位置:   article > 正文

C++王牌结构hash:哈希表开散列(哈希桶)的实现与应用_开散列 c++

开散列 c++

目录

一、开散列的概念

1.1开散列与闭散列比较

二、开散列/哈希桶的实现

2.1开散列实现

哈希函数的模板构造

哈希表节点构造

开散列增容

插入数据

2.2代码实现


一、开散列的概念

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

fe028ef67fc447009dc9a8cfeb08e470.png

从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。

1.1开散列与闭散列比较

应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

二、开散列/哈希桶的实现

2.1开散列实现

哈希函数的模板构造

当数据类型不是整数时,我们需要通过哈希函数将其转换为一个size_t类型的无符号整形然后%上哈希表的容量得出一个映射值,所以需要针对不同的数据类型,来构造不同的Hashfunc来将其转换为size_t类型,这时就要用到模板特化来处理数据,尤其是字符串类型。

哈希表节点构造

同时针对set和map的不同,我们需要将hash桶的模板可以满足两种不同类型的调用,所以在参数上也要设置两个参数,如果是set传参,就让两个参数都是K,如果是map传参,第一个参数是K,第二个参数则是pair<K,V>,而在构造哈希表的node时,不管是set还是map都只需要传第二个参数过去,而hashnode也只需要用一个template<class T>来进行接收就好,然后构造初始化出T _data和一个T* _next的指针来指向桶中下一个节点。

那为什么在传参时不直接只设置一个参数呢?因为在调用find时,需要传一个值进去查找,如果是set则直接查找,如果是map则需要取出hashnode中的first与之进行比较,所以必须设置两个模板参数。

开散列增容

桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对哈希表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个节点,再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数时,可以给哈希表增容。

插入数据

因为开散列每个位置都是一串单链表,所以在插入节点时,直接选择头插即可,头插的消耗和速度都是最小的。

2.2代码实现

  1. #pragma once
  2. #include<iostream>
  3. using namespace std;
  4. #include<vector>
  5. #include<string>
  6. template<class K>
  7. struct HashFunc
  8. {
  9. size_t operator()(const K& key)
  10. {
  11. return (size_t)key;
  12. }
  13. };
  14. // 特化
  15. template<>
  16. struct HashFunc<string>
  17. {
  18. size_t operator()(const string& s)
  19. {
  20. size_t hash = 0;
  21. for (auto e : s)
  22. {
  23. hash += e;
  24. hash *= 131;
  25. }
  26. return hash;
  27. }
  28. };
  29. namespace hash_bucket
  30. {
  31. //如果是unordered_set的话T=K
  32. //如果是unordered_map的话T=pair<K,V>
  33. template<class T>
  34. struct HashNode
  35. {
  36. HashNode<T>* _next;
  37. T _data;
  38. HashNode(const T& data)
  39. :_next(nullptr)
  40. ,_data(data)
  41. {}
  42. };
  43. // 前置声明,因为编译器编译时会向上进行查找,而iterator要去调用哈希表,所以需要提前进行前置声明
  44. template<class K, class T, class Keyoft, class Hash >
  45. class HashTable;
  46. //迭代器实现
  47. template<class K, class T, class Keyoft, class Hash >
  48. struct __HTIterator
  49. {
  50. typedef HashNode<T> Node;
  51. typedef HashTable<K, T, Keyoft, Hash> HT;
  52. typedef __HTIterator<K, T, Keyoft, Hash> Self;
  53. Node* _node;
  54. HT* _ht;
  55. __HTIterator(Node* node,HT* ht)
  56. :_node(node)
  57. ,_ht(ht)
  58. {}
  59. T& operator*()
  60. {
  61. return _node->_data;
  62. }
  63. Self& operator++()
  64. {
  65. //如果当前桶内还有节点
  66. if (_node->_next)
  67. {
  68. _node = _node->_next;
  69. }
  70. else
  71. {
  72. //当前桶找完,就去找下一个桶
  73. Keyoft kot;
  74. Hash hs;
  75. size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();
  76. hashi++;
  77. while (hashi < _ht->_tables.size())
  78. {
  79. if (_ht->_tables[hashi])
  80. {
  81. _node = _ht->_tables[hashi];
  82. break;
  83. }
  84. hashi++;
  85. }
  86. //如果后面没有桶
  87. if (hashi == _ht->_tables.size())
  88. {
  89. _node = nullptr;
  90. }
  91. }
  92. return *this;
  93. }
  94. bool operator!=(const Self& s)
  95. {
  96. return _node != s._node;
  97. }
  98. };
  99. //哈希桶搭建
  100. template<class K, class T,class Keyoft,class Hash>
  101. class HashTable
  102. {
  103. template<class K, class T, class KeyOfT, class Hash>
  104. friend struct __HTIterator;
  105. typedef HashNode<T> Node;
  106. public:
  107. typedef __HTIterator< K, T, Keyoft, Hash> iterator;
  108. HashTable()
  109. {
  110. _tables.resize(10, nullptr);
  111. _n = 0;
  112. }
  113. ~HashTable()
  114. {
  115. for (size_t i = 0; i < _tables.size(); i++)
  116. {
  117. Node* cur = _tables[i];
  118. while (cur)
  119. {
  120. Node* next = cur->_next;
  121. delete cur;
  122. cur = next;
  123. }
  124. _tables[i] = nullptr;
  125. }
  126. }
  127. iterator begin()
  128. {
  129. for (size_t i = 0; i < _tables.size(); i++)
  130. {
  131. // 找到第一个桶的第一个节点
  132. if (_tables[i])
  133. {
  134. return iterator(_tables[i], this);
  135. }
  136. }
  137. return end();
  138. }
  139. iterator end()
  140. {
  141. return iterator(nullptr, this);
  142. }
  143. //插入节点
  144. bool insert(const T& data)
  145. {
  146. Keyoft kot;
  147. if (Find(kot(data)))
  148. return false;
  149. Hash hs;
  150. //负载因子到1就扩容
  151. if (_n == _tables.size())
  152. {
  153. vector<Node*> newtables(_tables.size() * 2,nullptr);
  154. for (size_t i = 0; i < _tables.size(); i++)
  155. {
  156. Node* cur = _tables[i];
  157. while (cur)
  158. {
  159. Node* next = cur->_next;
  160. //头插到新表
  161. size_t hashi = hs(kot(cur->_data)) % newtables.size();
  162. newtables[hashi] = cur;
  163. cur = next;
  164. }
  165. _tables[i] = nullptr;
  166. }
  167. _tables.swap(newtables);
  168. }
  169. size_t hashi = hs(kot(data)) % _tables.size();
  170. Node* newnode = new Node(data);
  171. //头插
  172. newnode->_next = _tables[hashi];
  173. _tables[hashi] = newnode;
  174. ++_n;
  175. return true;
  176. }
  177. //查找
  178. Node* Find(const K& key)
  179. {
  180. Hash hs;
  181. Keyoft kot;
  182. size_t hashi = hs(key) % _tables.size();
  183. Node* prev = nullptr;
  184. Node* cur = _tables[hashi];
  185. while (cur)
  186. {
  187. if (kot(cur->_data) == key)
  188. return cur;
  189. cur = cur->_next;
  190. }
  191. return nullptr;
  192. }
  193. Node* Erase(const K& key)
  194. {
  195. Hash hs;
  196. Keyoft kot;
  197. size_t hashi = hs(key) % _tables.size();
  198. Node* prev = nullptr;
  199. Node* cur = _tables[hashi];
  200. while (cur)
  201. {
  202. if (kot(cur->_data) == key)
  203. {
  204. if (prev == nullptr)
  205. {
  206. _tables[hashi] = cur->next;
  207. }
  208. else
  209. {
  210. prev->_next = cur->_next;
  211. }
  212. }
  213. prev = cur;
  214. cur = cur->_next;
  215. }
  216. return false;
  217. }
  218. private:
  219. vector<Node*> _tables;//指针数组
  220. size_t _n;
  221. };
  222. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/865306
推荐阅读
  

闽ICP备14008679号