当前位置:   article > 正文

哈希桶——开放定址法_哈希开放定址法

哈希开放定址法

哈希表的迭代器:

迭代器模板介绍:

template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>

K:关键词类型

T:存储的数据类型

Ref:T& (operator*() 解引用函数的返回类型)

Ptr:T*      (operator->() 使用指针去操作成员)

KeyOfT:是外面哈希map (哈希map中存的是键值对 键值对的第一个数据就是key 第二个数据就是value 存储的数据) 和哈希set (哈希set中存的是value )

外部会提供调函数去调用关键词key

Hash:是哈希函数将K关键词转化为hashi哈希桶的位置进行存储 将key转化为整数的函数 为了确定哈希桶的位置

哈希迭代器的成员介绍:

  1. Node* _node;
  2. const HashTable<K, T, KeyOfT, Hash>* _pht;
  3. size_t _hashi;

迭代器中_node 是实现 operator*(),operator->(),operator!=().

_hashi:每一次桶的位置 operator++()会运用到进行节点的遍历 记录每次桶的位置方便遍历

_pht:哈希表获取哈希桶的数据参数,获取哈希表的成员数据,成员函数等,我个人认为这个成员很方便也很重要 

哈希表的默认构造:

  1. __HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
  2. :_node(node)
  3. ,_pht(pht)
  4. ,_hashi(hashi)
  5. {}
  6. __HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
  7. :_node(node)
  8. , _pht(pht)
  9. , _hashi(hashi)
  10. {}

利用初始化列表进行初始化,根据外面传的参数类型选择对应的默认构造函数,进行初始化构造。

哈希表迭代器中的操作符重载:

* -> !=

  1. Ref operator*()
  2. {
  3. return _node->_data;
  4. }
  5. Ptr operator->()
  6. {
  7. return &_node->_data;
  8. }
  9. bool operator!=(const Self& s)
  10. {
  11. return _node != s._node;
  12. }

 !=利用迭代器中存储的_node进行判断 判断是否为同一个指针。

* 对节点数据进行引用

-> 对节点数据地址进行返回 但是Ptr=T* (*和-> 相遇会抵消 所以也是数据)

++

  1. Self& operator++()
  2. {
  3. if (_node->_next)
  4. {
  5. // 当前桶还有节点,走到下一个节点
  6. _node = _node->_next;
  7. }
  8. else
  9. {
  10. // 当前桶已经走完了,找下一个桶开始
  11. //KeyOfT kot;
  12. //Hash hf;
  13. //size_t hashi = hf(kot(_node->_data)) % _pht._tables.size();
  14. ++_hashi;
  15. while (_hashi < _pht->_tables.size())
  16. {
  17. if (_pht->_tables[_hashi])
  18. {
  19. _node = _pht->_tables[_hashi];
  20. break;
  21. }
  22. ++_hashi;
  23. }
  24. if (_hashi == _pht->_tables.size())
  25. {
  26. _node = nullptr;
  27. }
  28. }
  29. return *this;
  30. }

 这里重点解释一下_node->_next为空之后的操作

1,第一个++hashi 为什么?

因为哈希表中的bigin() 会进行哈希桶的遍历 返回第一个不为空的哈希桶 (用该位置的节点,哈希表指针,哈希桶位置进行迭代器的赋值)但是begin()只会提供第一个不为空的哈希桶位置 不会将哈希桶进行向后遍历 如果该桶的单链表遍历完毕 到末尾nullptr 就应该找下一个通的位置 如果下一个桶的位置也为空 再向后找

第一个++hashi 就是单链表遍历完毕遇到空进行下一个哈希桶的查找
第二个++hashi 是该哈希桶位置本身为空 进行向后的查找

2.找到不为空的哈希表位置之后 再进单链表的迭代遍历

返回的就是迭代器*this

哈希表模板的介绍:

template<class K, class T, class KeyOfT, class Hash>

K:关键词类型

T:存储数据类型

KeyOfT:外部提取key所配的专用函数

Hash: 将key转化为整数的函数 为了确定哈希桶的位置

哈希表中的友元:

  1. template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  2. friend struct __HTIterator;

由于哈希表迭代器成员中存在哈希表指针,二哈希表指针会访问哈希表成员,所以将哈希表迭代器设置为哈希表的友元。 

哈希表中类型的typedef

  1. typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
  2. typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

根据外部的调用进行对应的调用。const迭代器就调用const对应的迭代器。 

哈希表的节点:

  1. template<class T>
  2. struct HashNode
  3. {
  4. HashNode<T>* _next;
  5. T _data;
  6. HashNode(const T& data)
  7. :_data(data)
  8. ,_next(nullptr)
  9. {}
  10. };

由于哈希桶是单链表构成的所以类节点的成员为该节点类型的指针 和数据 ,将节点初始化为空。

哈希表的查找:

  1. iterator Find(const K& key)
  2. {
  3. Hash hf;
  4. KeyOfT kot;
  5. size_t hashi = hf(key) % _tables.size();
  6. Node* cur = _tables[hashi];
  7. while (cur)
  8. {
  9. if (kot(cur->_data) == key)
  10. {
  11. return iterator(cur, this, hashi);
  12. }
  13. cur = cur->_next;
  14. }
  15. return end();
  16. }

这里就是简单的单链表遍历查找,利用哈希函数和KeyOfT外部函数进行关键词的查找,存在返回迭代器,不存在返回end(). 

哈希表的插入:

  1. pair<iterator, bool> Insert(const T& data)
  2. {
  3. Hash hf;
  4. KeyOfT kot;
  5. iterator it = Find(kot(data));
  6. if (it != end())
  7. return make_pair(it, false);
  8. // 负载因子最大到1
  9. if (_n == _tables.size())
  10. {
  11. vector<Node*> newTables;
  12. newTables.resize(_tables.size() * 2, nullptr);
  13. // 遍历旧表
  14. for (size_t i = 0; i < _tables.size(); i++)
  15. {
  16. Node* cur = _tables[i];
  17. while(cur)
  18. {
  19. Node* next = cur->_next;
  20. // 挪动到映射的新表
  21. size_t hashi = hf(kot(cur->_data)) % newTables.size();
  22. cur->_next = newTables[i];
  23. newTables[i] = cur;
  24. cur = next;
  25. }
  26. _tables[i] = nullptr;
  27. }
  28. _tables.swap(newTables);
  29. }
  30. size_t hashi = hf(kot(data)) % _tables.size();
  31. Node* newnode = new Node(data);
  32. // 头插
  33. newnode->_next = _tables[hashi];
  34. _tables[hashi] = newnode;
  35. ++_n;
  36. return make_pair(iterator(newnode, this, hashi), true);
  37. }

首先判断该关键词是存在,如果存在就返回存在数据位置的迭代器和false;

 这里面的扩容是插入元素与哈希桶的数量相同就扩容,这里不是固定的,你可以判断每个哈希桶的大小最大不超过多少,再去扩容,这里方法很自由,没有固定的。

对于新表元素的插入,新表元素的插入方法与旧表类似,所以就可以直接赋用旧表的插入方法,这里很容易被看作递归,但不是递归,是代码赋用。

将新表插入完毕之后 再将旧表数据与新表数据进行交换,这里新表出了作用域就调用其对应的析构函数进行析构。大大的方便。

插入成功返回新节点位置的迭代器迭代器进行赋值(存在时的赋值在find函数中有涉及),和true.

哈希表的析构与构造:

  1. HashTable()
  2. {
  3. _tables.resize(10);
  4. }
  5. ~HashTable()
  6. {
  7. for (size_t i = 0; i < _tables.size(); i++)
  8. {
  9. Node* cur = _tables[i];
  10. while (cur)
  11. {
  12. Node* next = cur->_next;
  13. delete cur;
  14. cur = next;
  15. }
  16. _tables[i] = nullptr;
  17. }
  18. }

哈希表的默认构造函数:对vector进行初始容量的 扩容;

哈希表的析构函数:从第一个桶位置开始进行遍历 跳过nullptr位置 将存在数据的位置进行单链表遍历删除 删除完单链表之后 再将该位置置为空。

哈希表的删除:

        哈希表节点的删除可以分为两种情况:

        第一种,删除哈希桶的头节点

        第二种,删除除头结点的任意节点

通过哈希函数将key转换为关键词,再遍历哈希桶(单链表)利用KeyOfT将数据进行提取找到要删除的数据 再进行上面两种情况的判断。

  1. bool Erase(const K& key)
  2. {
  3. Hash hf;
  4. KeyOfT kot;
  5. size_t hashi = hf(key) % _tables.size();
  6. Node* prev = nullptr;
  7. Node* cur = _tables[hashi];
  8. while (cur)
  9. {
  10. if (kot(cur->_data) == key)
  11. {
  12. if (prev == nullptr)
  13. {
  14. _tables[hashi] = cur->_next;
  15. }
  16. else
  17. {
  18. prev->_next = cur->_next;
  19. }
  20. delete cur;
  21. return true;
  22. }
  23. prev = cur;
  24. cur = cur->_next;
  25. }
  26. return false;
  27. }

哈希桶长度,数量,最大长度,平均桶长的统计: 

  1. void Some()
  2. {
  3. size_t bucketSize = 0;
  4. size_t maxBucketLen = 0;
  5. size_t sum = 0;
  6. double averageBucketLen = 0;
  7. for (size_t i = 0; i < _tables.size(); i++)
  8. {
  9. Node* cur = _tables[i];
  10. if (cur)
  11. {
  12. ++bucketSize;
  13. }
  14. size_t bucketLen = 0;
  15. while (cur)
  16. {
  17. ++bucketLen;
  18. cur = cur->_next;
  19. }
  20. sum += bucketLen;
  21. if (bucketLen > maxBucketLen)
  22. {
  23. maxBucketLen = bucketLen;
  24. }
  25. }
  26. averageBucketLen = (double)sum / (double)bucketSize;
  27. printf("all bucketSize:%d\n", _tables.size());
  28. printf("bucketSize:%d\n", bucketSize);
  29. printf("maxBucketLen:%d\n", maxBucketLen);
  30. printf("averageBucketLen:%lf\n\n", averageBucketLen);
  31. }

上面代码就是简单的遍历,不做过多讲解。

完整代码:

  1. namespace hash_bucket
  2. {
  3. template<class T>
  4. struct HashNode
  5. {
  6. HashNode<T>* _next;
  7. T _data;
  8. HashNode(const T& data)
  9. :_data(data)
  10. ,_next(nullptr)
  11. {}
  12. };
  13. // 前置声明
  14. template<class K, class T, class KeyOfT, class Hash>
  15. class HashTable;
  16. template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  17. struct __HTIterator
  18. {
  19. typedef HashNode<T> Node;
  20. typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
  21. Node* _node;
  22. const HashTable<K, T, KeyOfT, Hash>* _pht;
  23. // vector<Node*> * _ptb;
  24. size_t _hashi;
  25. __HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
  26. :_node(node)
  27. ,_pht(pht)
  28. ,_hashi(hashi)
  29. {}
  30. __HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
  31. :_node(node)
  32. , _pht(pht)
  33. , _hashi(hashi)
  34. {}
  35. Self& operator++()
  36. {
  37. if (_node->_next)
  38. {
  39. // 当前桶还有节点,走到下一个节点
  40. _node = _node->_next;
  41. }
  42. else
  43. {
  44. // 当前桶已经走完了,找下一个桶开始
  45. //KeyOfT kot;
  46. //Hash hf;
  47. //size_t hashi = hf(kot(_node->_data)) % _pht._tables.size();
  48. ++_hashi;
  49. while (_hashi < _pht->_tables.size())
  50. {
  51. if (_pht->_tables[_hashi])
  52. {
  53. _node = _pht->_tables[_hashi];
  54. break;
  55. }
  56. ++_hashi;
  57. }
  58. if (_hashi == _pht->_tables.size())
  59. {
  60. _node = nullptr;
  61. }
  62. }
  63. return *this;
  64. }
  65. Ref operator*()
  66. {
  67. return _node->_data;
  68. }
  69. Ptr operator->()
  70. {
  71. return &_node->_data;
  72. }
  73. bool operator!=(const Self& s)
  74. {
  75. return _node != s._node;
  76. }
  77. };
  78. // unordered_set -> Hashtable<K, K>
  79. // unordered_map -> Hashtable<K, pair<K, V>>
  80. template<class K, class T, class KeyOfT, class Hash>
  81. class HashTable
  82. {
  83. typedef HashNode<T> Node;
  84. template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
  85. friend struct __HTIterator;
  86. public:
  87. typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
  88. typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;
  89. iterator begin()
  90. {
  91. for (size_t i = 0; i < _tables.size(); i++)
  92. {
  93. if (_tables[i])
  94. {
  95. return iterator(_tables[i], this, i);
  96. }
  97. }
  98. return end();
  99. }
  100. iterator end()
  101. {
  102. return iterator(nullptr, this, -1);
  103. }
  104. const_iterator begin() const
  105. {
  106. for (size_t i = 0; i < _tables.size(); i++)
  107. {
  108. if (_tables[i])
  109. {
  110. return const_iterator(_tables[i], this, i);
  111. }
  112. }
  113. return end();
  114. }
  115. // this-> const HashTable<K, T, KeyOfT, Hash>*
  116. const_iterator end() const
  117. {
  118. return const_iterator(nullptr, this, -1);
  119. }
  120. HashTable()
  121. {
  122. _tables.resize(10);
  123. }
  124. ~HashTable()
  125. {
  126. for (size_t i = 0; i < _tables.size(); i++)
  127. {
  128. Node* cur = _tables[i];
  129. while (cur)
  130. {
  131. Node* next = cur->_next;
  132. delete cur;
  133. cur = next;
  134. }
  135. _tables[i] = nullptr;
  136. }
  137. }
  138. pair<iterator, bool> Insert(const T& data)
  139. {
  140. Hash hf;
  141. KeyOfT kot;
  142. iterator it = Find(kot(data));
  143. if (it != end())
  144. return make_pair(it, false);
  145. // 负载因子最大到1
  146. if (_n == _tables.size())
  147. {
  148. vector<Node*> newTables;
  149. newTables.resize(_tables.size() * 2, nullptr);
  150. // 遍历旧表
  151. for (size_t i = 0; i < _tables.size(); i++)
  152. {
  153. Node* cur = _tables[i];
  154. while(cur)
  155. {
  156. Node* next = cur->_next;
  157. // 挪动到映射的新表
  158. size_t hashi = hf(kot(cur->_data)) % newTables.size();
  159. cur->_next = newTables[i];
  160. newTables[i] = cur;
  161. cur = next;
  162. }
  163. _tables[i] = nullptr;
  164. }
  165. _tables.swap(newTables);
  166. }
  167. size_t hashi = hf(kot(data)) % _tables.size();
  168. Node* newnode = new Node(data);
  169. // 头插
  170. newnode->_next = _tables[hashi];
  171. _tables[hashi] = newnode;
  172. ++_n;
  173. return make_pair(iterator(newnode, this, hashi), true);
  174. }
  175. iterator Find(const K& key)
  176. {
  177. Hash hf;
  178. KeyOfT kot;
  179. size_t hashi = hf(key) % _tables.size();
  180. Node* cur = _tables[hashi];
  181. while (cur)
  182. {
  183. if (kot(cur->_data) == key)
  184. {
  185. return iterator(cur, this, hashi);
  186. }
  187. cur = cur->_next;
  188. }
  189. return end();
  190. }
  191. bool Erase(const K& key)
  192. {
  193. Hash hf;
  194. KeyOfT kot;
  195. size_t hashi = hf(key) % _tables.size();
  196. Node* prev = nullptr;
  197. Node* cur = _tables[hashi];
  198. while (cur)
  199. {
  200. if (kot(cur->_data) == key)
  201. {
  202. if (prev == nullptr)
  203. {
  204. _tables[hashi] = cur->_next;
  205. }
  206. else
  207. {
  208. prev->_next = cur->_next;
  209. }
  210. delete cur;
  211. return true;
  212. }
  213. prev = cur;
  214. cur = cur->_next;
  215. }
  216. return false;
  217. }
  218. void Some()
  219. {
  220. size_t bucketSize = 0;
  221. size_t maxBucketLen = 0;
  222. size_t sum = 0;
  223. double averageBucketLen = 0;
  224. for (size_t i = 0; i < _tables.size(); i++)
  225. {
  226. Node* cur = _tables[i];
  227. if (cur)
  228. {
  229. ++bucketSize;
  230. }
  231. size_t bucketLen = 0;
  232. while (cur)
  233. {
  234. ++bucketLen;
  235. cur = cur->_next;
  236. }
  237. sum += bucketLen;
  238. if (bucketLen > maxBucketLen)
  239. {
  240. maxBucketLen = bucketLen;
  241. }
  242. }
  243. averageBucketLen = (double)sum / (double)bucketSize;
  244. printf("all bucketSize:%d\n", _tables.size());
  245. printf("bucketSize:%d\n", bucketSize);
  246. printf("maxBucketLen:%d\n", maxBucketLen);
  247. printf("averageBucketLen:%lf\n\n", averageBucketLen);
  248. }
  249. private:
  250. vector<Node*> _tables;
  251. size_t _n = 0;
  252. };
  253. }

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

闽ICP备14008679号