当前位置:   article > 正文

C++|哈希结构封装unordered_set和unordered_map

C++|哈希结构封装unordered_set和unordered_map

目录

一、改造哈希表

1.1模板参数列表的改造

1.2增加哈希表迭代器操作

1.2.1哈希表迭代器框架

1.2.2operator*() && operator->()

1.2.3operator++()

1.2.4operator--()

1.2.5operator==() && operator!=()

二、如何用哈希表搭配unordered_map和unordered_set(仿函数)

三、哈希表封装unordered_map和unordered_set(简易版)

3.1哈希表的实现(HashTable.h)

3.2unordered_map的模拟实现(My_Unordered_Map.h)

3.3unordered_set的模拟实现(My_Unordered_Set.h)

3.4测试(test.cpp)


上一篇章,学习了unordered系列容器的使用,以及哈希结构,那么这一篇章将通过哈希结构来封装unordered系列容器,来进一步的学习他们的使用以及理解为何是如此使用。其实,哈希表的封装方式和红黑树的封装方式形式上是差不多的,如果有了红黑树的封装理解经验,我相信在理解哈希封装的过程会减少负担的。当然,在使用哈希结构中采用的是更具代表的哈希桶,接下来进行封装。

一、改造哈希表

1.1模板参数列表的改造

同理模板参数列表的改造跟红黑树的改造差不多。

K:关键码类型

V:不同容器v的容器类型不同,如果是unordered_map,v代表一个键值对,如果是unordered_set,v为k

KeyOfT:因为v的类型不同,通过keyOfT取key的方式就不同 。

Hash:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将字符串类型转化为整形数字才能取模。

1.2增加哈希表迭代器操作

1.2.1哈希表迭代器框架

  1. //前置声明,迭代器中才能定义HashTable对象
  2. template<class K, class T, class KeyOfT, class hash>
  3. class HashTable;
  4. template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
  5. struct _HTIterator
  6. {
  7. typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;//方便作为接收迭代器操作的返回值的类型
  8. typedef HashNode<T> Node;
  9. typedef Node* PNode;
  10. PNode _node;//定义节点指针,方便访问其成员来完成迭代器的操作
  11. HashTable<K, T, KeyOfT, hash>* _ht;//定义哈希表指针,方便使用其成员来完成迭代器的操作
  12. size_t _hashi;//记录迭代器访问的位置
  13. _HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
  14. :_node(node)
  15. ,_ht(ht)
  16. ,_hashi(hashi)
  17. {}
  18. //....
  19. };

1.2.2operator*() && operator->()

  1. Ref operator*()
  2. {
  3. return _node->_data;
  4. }
  5. Ptr operator->()
  6. {
  7. return &(_node->_data);
  8. }

1.2.3operator++()

  1. Self& operator++()
  2. {
  3. if (_node->_next)//下一个元素不为空
  4. {
  5. _node = _node->_next;
  6. }
  7. else
  8. {
  9. /*hash hs;
  10. KeyOfT oft;
  11. int hashi = hs(oft(this->_data) % _tables.size());*/
  12. //寻找下一个不为空的桶
  13. ++_hashi;
  14. while(_hashi < _ht->_tables.size())
  15. {
  16. if (_ht->_tables[_hashi] != nullptr)
  17. {
  18. _node = _ht->_tables[_hashi];
  19. break;
  20. }
  21. ++_hashi;
  22. }
  23. //到末尾还没有找到不为空的桶
  24. if(_hashi == _ht->_tables.size())
  25. _node = nullptr;
  26. }
  27. return *this;
  28. }

1.2.4operator--()

  1. Self operator--()
  2. {
  3. PNode cur = _ht->_tables[_hashi];
  4. //当前节点就是第一个节点
  5. if (cur->_next == nullptr)
  6. {
  7. //寻找上一个非空桶
  8. --_hashi;
  9. while (_hashi)
  10. {
  11. //寻找该非空桶的最后一个元素
  12. if (_ht->_tables[_hashi])
  13. {
  14. cur = _ht->_tables[_hashi];
  15. while (cur->_next)
  16. {
  17. cur = cur->_next;
  18. }
  19. _node = cur;
  20. break;
  21. }
  22. _hashi--;
  23. }
  24. }
  25. else
  26. {
  27. while (cur->_next != _node)
  28. {
  29. cur = cur->_next;
  30. }
  31. _node = cur;
  32. }
  33. return *this;
  34. }

1.2.5operator==() && operator!=()

  1. bool operator==(const Self& x)
  2. {
  3. return _node == x._node;//由于没有重复的元素,直接比较节点的地址
  4. }
  5. bool operator!=(const Self& x)
  6. {
  7. return _node != x._node;
  8. }

二、如何用哈希表搭配unordered_map和unordered_set(仿函数)

我们可以用两张哈希表分别封装一份unordered_map和一份unordered_set,但是这样做的效果就带来了代码冗余。为了减少代码冗余,模拟跟库保持用一张哈希表封装unordered_map和unordered_set,但是该如何做到套用一张表呢,我们来进一步分析。

 首先对于unordered_map而言,其存放的节点值是pair,而对于unordered_set存放的是key,这对于哈希表节点的实现到是没啥问题,但是对于哈希表内部的构造,是需要查询插入的位置,就需要进行比较,若将比较实现成key的比较,那么对于pair类型又该如何比较,虽然知道比较的也是pair中的key,但是如何做到既满足unordered_set中的key类型比较,又满足pair类型中的key比较,总不能干两份代码吧。这个时候,我们的仿函数又派上用场了,对于unordered_set和unordered_map中都构造一个仿函数,分别表示取到unordered_set的key,和unordered_map中pair中的key,那么哈希表中的比较,就可以换成仿函数的比较,当往unordered_set中插入元素进行比较,调用的就是unordered_set的仿函数,当往unordered_map中插入元素进行比较,调用的就是unordered_map的仿函数从而达到回调。用一张图来进行表示,如图:

三、哈希表封装unordered_map和unordered_set(简易版)

3.1哈希表的实现(HashTable.h)

  1. //哈希桶/链地址法
  2. namespace Hash_Bucket
  3. {
  4. template<class T>
  5. struct HashNode
  6. {
  7. HashNode(const T& data)
  8. :_next(nullptr)
  9. ,_data(data)
  10. {}
  11. HashNode<T>* _next;
  12. T _data;
  13. };
  14. //前置声明,迭代器中才能定义HashTable对象
  15. template<class K, class T, class KeyOfT, class hash>
  16. class HashTable;
  17. template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
  18. struct _HTIterator
  19. {
  20. typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;
  21. typedef HashNode<T> Node;
  22. typedef Node* PNode;
  23. PNode _node;
  24. HashTable<K, T, KeyOfT, hash>* _ht;
  25. size_t _hashi;
  26. _HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
  27. :_node(node)
  28. ,_ht(ht)
  29. ,_hashi(hashi)
  30. {}
  31. Self& operator++()
  32. {
  33. if (_node->_next)
  34. {
  35. _node = _node->_next;
  36. }
  37. else
  38. {
  39. /*hash hs;
  40. KeyOfT oft;
  41. int hashi = hs(oft(this->_data) % _tables.size());*/
  42. ++_hashi;
  43. while(_hashi < _ht->_tables.size())
  44. {
  45. if (_ht->_tables[_hashi] != nullptr)
  46. {
  47. _node = _ht->_tables[_hashi];
  48. break;
  49. }
  50. ++_hashi;
  51. }
  52. if(_hashi == _ht->_tables.size())
  53. _node = nullptr;
  54. }
  55. return *this;
  56. }
  57. Self operator--()
  58. {
  59. PNode cur = _ht->_tables[_hashi];
  60. //当前节点就是第一个节点
  61. if (cur->_next == nullptr)
  62. {
  63. //寻找上一个非空桶
  64. --_hashi;
  65. while (_hashi)
  66. {
  67. //寻找该非空桶的最后一个元素
  68. if (_ht->_tables[_hashi])
  69. {
  70. cur = _ht->_tables[_hashi];
  71. while (cur->_next)
  72. {
  73. cur = cur->_next;
  74. }
  75. _node = cur;
  76. break;
  77. }
  78. _hashi--;
  79. }
  80. }
  81. else
  82. {
  83. while (cur->_next != _node)
  84. {
  85. cur = cur->_next;
  86. }
  87. _node = cur;
  88. }
  89. return *this;
  90. }
  91. Ref operator*()
  92. {
  93. return _node->_data;
  94. }
  95. Ptr operator->()
  96. {
  97. return &(_node->_data);
  98. }
  99. bool operator==(const Self& x)
  100. {
  101. return _node == x._node;//由于没有重复的元素,直接比较节点的地址
  102. }
  103. bool operator!=(const Self& x)
  104. {
  105. return _node != x._node;
  106. }
  107. };
  108. template<class K,class T,class KeyOfT,class hash>
  109. class HashTable
  110. {
  111. //声明友元,迭代器方可访问该类中的私有成员
  112. template<class K, class T, class Ref, class Ptr, class KeyOfT,class hash>
  113. friend struct _HTIterator;
  114. public:
  115. typedef _HTIterator<K, T,T&, T*, KeyOfT,hash> iterator;
  116. typedef _HTIterator<K, T, const T&, const T*, KeyOfT, hash> const_iterator;
  117. typedef HashNode<T> Node;
  118. typedef Node* PNode;
  119. HashTable(size_t size = 10)
  120. {
  121. _tables.resize(size);
  122. }
  123. iterator begin()
  124. {
  125. for (int i = 0; i < _tables.size(); i++)
  126. {
  127. if (_tables[i] != nullptr)
  128. return iterator(_tables[i], this, i);
  129. }
  130. return end();
  131. }
  132. iterator end()
  133. {
  134. return iterator(nullptr,this,-1);
  135. }
  136. const_iterator begin() const
  137. {
  138. for (int i = 0; i < _tables.size(); i++)
  139. {
  140. if (_tables[i] != nullptr)
  141. return iterator(_tables[i], this, i);
  142. }
  143. return end();
  144. }
  145. const_iterator end() const
  146. {
  147. return iterator(nullptr, this, -1);
  148. }
  149. iterator Find(const K& key)
  150. {
  151. KeyOfT oft;
  152. hash hs;
  153. int hashi = hs(key) % _tables.size();
  154. PNode cur = _tables[hashi];
  155. while (cur)
  156. {
  157. if(hs(oft(cur->_data)) == hs(key))
  158. return iterator(cur, this, hashi);
  159. cur = cur->_next;
  160. }
  161. return iterator(nullptr, this, -1);
  162. }
  163. pair<iterator,bool> Insert(T data)
  164. {
  165. hash hs;
  166. KeyOfT oft;
  167. iterator it = Find(oft(data));
  168. if (it != end())
  169. return make_pair(it,false);
  170. //扩容
  171. if (_n == _tables.size())
  172. {
  173. vector<PNode> _newHT(_tables.size()*2);
  174. for (int i = 0; i < _tables.size(); i++)
  175. {
  176. PNode cur = _tables[i];
  177. while (cur)
  178. {
  179. PNode next = cur->_next;
  180. int hashi = hs(oft(_tables[i]->_data)) % _newHT.size();
  181. //元素一直在变,所以不能用_tables[i]做代表
  182. /*_tables[i]->_next = nullptr;
  183. _newHT[hashi] = _tables[i];*/
  184. cur->_next = nullptr;
  185. _newHT[hashi] = cur;
  186. cur = next;
  187. }
  188. _tables[i] = nullptr;
  189. }
  190. _tables.swap(_newHT);
  191. }
  192. //头插
  193. PNode newnode =new Node(data);
  194. int hashi = hs(oft(data)) % _tables.size();
  195. newnode->_next = _tables[hashi];
  196. _tables[hashi] = newnode;
  197. ++_n;
  198. return make_pair(iterator(newnode, this, hashi),true);
  199. }
  200. bool Erase(const K& key)
  201. {
  202. KeyOfT oft;
  203. iterator it = Find(key);
  204. if (it == end())
  205. return false;
  206. hash hs;
  207. int hashi = hs(key) % _tables.size();
  208. PNode cur = _tables[hashi];
  209. PNode parent = nullptr;
  210. while (hs(oft(cur->_data)) != hs(key))
  211. {
  212. parent = cur;
  213. cur = cur->_next;
  214. }
  215. if (parent)
  216. {
  217. delete cur;
  218. parent->_next = nullptr;
  219. }
  220. else
  221. {
  222. delete cur;
  223. _tables[hashi] = nullptr;
  224. }
  225. return true;
  226. }
  227. ~HashTable()
  228. {
  229. for (int i = 0; i < _tables.size(); i++)
  230. {
  231. PNode cur = _tables[i];
  232. while (cur)
  233. {
  234. PNode next = cur->_next;
  235. delete cur;
  236. cur = next;
  237. }
  238. _tables[i] = nullptr;
  239. }
  240. }
  241. //另外加的,为了测试用
  242. void Some()
  243. {
  244. size_t bucketSize = 0;
  245. size_t maxBucketLen = 0;
  246. size_t sum = 0;
  247. double averageBucketLen = 0;
  248. for (size_t i = 0; i < _tables.size(); i++)
  249. {
  250. Node* cur = _tables[i];
  251. if (cur)
  252. {
  253. ++bucketSize;
  254. }
  255. size_t bucketLen = 0;
  256. while (cur)
  257. {
  258. ++bucketLen;
  259. cur = cur->_next;
  260. }
  261. sum += bucketLen;
  262. if (bucketLen > maxBucketLen)
  263. {
  264. maxBucketLen = bucketLen;
  265. }
  266. }
  267. averageBucketLen = (double)sum / (double)bucketSize;
  268. printf("all bucketSize:%d\n", _tables.size());
  269. printf("bucketSize:%d\n", bucketSize);
  270. printf("maxBucketLen:%d\n", maxBucketLen);
  271. printf("averageBucketLen:%lf\n\n", averageBucketLen);
  272. }
  273. private:
  274. vector<HashNode<T>*> _tables;
  275. size_t _n = 0;
  276. };
  277. }

3.2unordered_map的模拟实现(My_Unordered_Map.h)

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace Hash_Bucket
  4. {
  5. template<class K,class V,class Hash = HashFunc<K>>
  6. class unordered_map
  7. {
  8. public:
  9. struct KeyOfTMap
  10. {
  11. const K& operator()(const pair<const K, V>& data)
  12. {
  13. return data.first;
  14. }
  15. };
  16. typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::iterator iterator;
  17. typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::const_iterator const_iterator;
  18. iterator begin()
  19. {
  20. return _hs.begin();
  21. }
  22. iterator end()
  23. {
  24. return _hs.end();
  25. }
  26. const_iterator begin() const
  27. {
  28. return _hs.begin();
  29. }
  30. const_iterator end() const
  31. {
  32. return _hs.end();
  33. }
  34. pair<iterator, bool> insert(const pair<K,V>& data)
  35. {
  36. return _hs.Insert(data);
  37. }
  38. V& operator[](const K& key)
  39. {
  40. pair<iterator, bool> ret = _hs.Insert(make_pair(key,V()));
  41. return ret.first->second;
  42. }
  43. bool erase(const K& key)
  44. {
  45. return _hs.Erase(key);
  46. }
  47. iterator find(const K& key)
  48. {
  49. return _hs.Find(key);
  50. }
  51. private:
  52. HashTable<K, pair<const K,V>, KeyOfTMap, Hash> _hs;
  53. };
  54. void test_map()
  55. {
  56. unordered_map<string, string> dict;
  57. dict.insert(make_pair("sort", ""));
  58. dict.insert(make_pair("string", ""));
  59. dict.insert(make_pair("insert", ""));
  60. for (auto& kv : dict)
  61. {
  62. //kv.first += 'x';
  63. kv.second += 'x';
  64. cout << kv.first << ":" << kv.second << endl;
  65. }
  66. cout << endl;
  67. string arr[] = { "你", " ","好", " ", "吗", " ", "你", "吃", " ", "了", "饭", "吗", "?" };
  68. unordered_map<string, int> count_map;
  69. for (auto& e : arr)
  70. {
  71. count_map[e]++;
  72. }
  73. for (auto& kv : count_map)
  74. {
  75. cout << kv.first << ":" << kv.second << endl;
  76. }
  77. cout << endl;
  78. }
  79. }

3.3unordered_set的模拟实现(My_Unordered_Set.h)

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace Hash_Bucket
  4. {
  5. template<class K,class Hash = HashFunc<K>>
  6. class unordered_set
  7. {
  8. public:
  9. struct KeyOfTSet
  10. {
  11. const K& operator()(const K& key)
  12. {
  13. return key;
  14. }
  15. };
  16. typedef typename HashTable<K, const K, KeyOfTSet, Hash>::iterator iterator;
  17. typedef typename HashTable<K, const K, KeyOfTSet, Hash>::const_iterator const_iterator;
  18. iterator begin()
  19. {
  20. return _hs.begin();
  21. }
  22. iterator end()
  23. {
  24. return _hs.end();
  25. }
  26. const_iterator begin() const
  27. {
  28. return _hs.begin();
  29. }
  30. const_iterator end() const
  31. {
  32. return _hs.end();
  33. }
  34. pair<iterator, bool> insert(const K& key)
  35. {
  36. return _hs.Insert(key);
  37. }
  38. bool erase(const K& key)
  39. {
  40. return _hs.Erase(key);
  41. }
  42. iterator find(const K& key)
  43. {
  44. return _hs.Find(key);
  45. }
  46. void some()
  47. {
  48. _hs.Some();
  49. }
  50. private:
  51. HashTable<K, const K, KeyOfTSet, Hash> _hs;
  52. };
  53. void test_set()
  54. {
  55. unordered_set<int> us;
  56. us.insert(5);
  57. us.insert(15);
  58. us.insert(52);
  59. us.insert(3);
  60. unordered_set<int>::iterator it = us.begin();
  61. while (it != us.end())
  62. {
  63. //*it += 5;
  64. cout << *it << " ";
  65. ++it;
  66. }
  67. cout << endl;
  68. for (auto e : us)
  69. {
  70. cout << e << " ";
  71. }
  72. cout << endl;
  73. us.some();
  74. }
  75. }

3.4测试(test.cpp)

  1. #include "My_Unordered_Map.h"
  2. #include "My_Unordered_Set.h"
  3. int main()
  4. {
  5. Hash_Bucket::test_map();
  6. Hash_Bucket::test_set();
  7. return 0;
  8. }

输出结果:

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

闽ICP备14008679号