当前位置:   article > 正文

C++:采用哈希表封装unordered_map和unordered_set_c++哈希表封装什么

c++哈希表封装什么

目录

一. 如何使用一张哈希表封装unordered_map和unordered_set

二. 哈希表迭代器的实现

2.1 迭代器成员变量及应当实现的功能

2.2 operator++函数

2.3 operator*和operator->函数

2.4 operator!=和operator==函数

2.5 begin()和end()

2.6哈希表迭代器实现代码

三. unordered_map和unordered_set的实现

附录:哈希表封装unorder_map和unordered_set完整版代码


一. 如何使用一张哈希表封装unordered_map和unordered_set

采用一张哈希表封装unordered_map和unordered_set的方法与采用一颗红黑树封装map和set的模型基本完全一样。

哈希表类HashTable有四个模板参数,分别为:键值Key的数据类型K、与键值Key匹配的数据的类型V、哈希仿函数函数类Hash以及用于提取键值的仿函数类KeyOfV。

  • 当哈希表封装用于unordered_map时,K-V构成键值对,作为哈希表中存储的数据的类型。当用于封装unordered_set时,V与K相同,哈希表中每个节点存储一个类型为K的数据,此时K和V实例化后的类型相同。
  • Hash为哈希仿函数,用于将键值Key转换为size_t(如string类型的键值,就需要相应的算法将其表征为size_t),以便通过哈希函数计算其存储的位置。
  • KeyOfV用于在提取键值Key,在unordered_map下,提取键值对的first,在unordered_set下,直接取Key。
图1.1 HashTable与unordered_map和unordered_set模板参数之间的关系

二. 哈希表迭代器的实现

2.1 迭代器成员变量及应当实现的功能

应当有两个成员变量:指向当前节点的指针_cur和哈希表类对象指针_ptable。成员函数及所实现的功能应当包括:

  • operator++函数:哈希表下一个有效节点的迭代器。
  • operator->和operator*,用于访问节点数据_data。
  • operator==和operator!=,用于判断两个迭代器是否表示同一个哈希表节点。

2.2 operator++函数

函数功能为找哈希表下一个有效位置,并返回那个位置的迭代器。要分两种情况讨论:

  1. 如果_cur->_next节点不是nullptr,则表明当前的_cur不是目前桶的最下面的节点,直接将_cur更新为_cur->next,然后返回*this即可。
  2. 如何_cur->next是nullptr,则表明下一个有效节点不在当前哈希桶,要么没有下一个有效节点,如果有就是_hashTable某个桶最上面位置的节点。因此只需向后遍历_hashTable[i],遇到不为空或结尾即可。
图2.1 operator++情况1
图2.2 operator++情况2

代码2.1:迭代器operator++函数

  1. //Self&表示迭代器本身类型的引用
  2. //迭代器自加函数
  3. Self& operator++()
  4. {
  5. //分两种情况讨论:cur所在的桶的next是否为空
  6. //_cur的下一个节点不为空
  7. if (_cur->_next)
  8. {
  9. _cur = _cur->_next;
  10. return *this;
  11. }
  12. else //_cur的_next节点为空,++要去到其他桶
  13. {
  14. Hash hash;
  15. KeyOfV kofv;
  16. size_t hashi = hash(kofv(_cur->_data)) % _ptable->_hashTable.size();
  17. for (size_t i = hashi + 1; i < _ptable->_hashTable.size(); ++i)
  18. {
  19. _cur = _ptable->_hashTable[i];
  20. if (_cur)
  21. {
  22. return *this;
  23. }
  24. }
  25. return *this;
  26. }
  27. }

2.3 operator*和operator->函数

这两个函数都是用于返回节点数据的,不同在于:operator*返回节点数据_data本身的引用,operator->返回_data的地址。

代码2.2:operator*和operator->函数

  1. //V为哈希表存储的数据的类型
  2. V& operator*()
  3. {
  4. return _cur->_data;
  5. }
  6. V* operator->()
  7. {
  8. return &_cur->_data;
  9. }

2.4 operator!=和operator==函数

如果两个迭代器表示不用的哈希表类对象的节点位置,那么他们的_cur一定不同,因此,只需要比较两个迭代器的_cur是否相同即可。

代码2.3:operator!=和operator==函数

  1. //迭代器相等判断函数
  2. bool operator==(const Self& it)
  3. {
  4. return _cur == it._cur;
  5. }
  6. //迭代器不等判断函数
  7. bool operator!=(const Self& it)
  8. {
  9. return _cur != it._cur;
  10. }

2.5 begin()和end()

begin()表示第一个有效节点的位置,用nullptr表示最后一个有效节点后面的位置end()。

图2.3 begin()和end()

2.6哈希表迭代器实现代码

  1. //哈希表类的声明
  2. template<class K, class V, class Hash, class KeyOfV>
  3. class HashTable;
  4. template<class K, class V, class Hash, class KeyOfV>
  5. struct __HashIterator
  6. {
  7. typedef HashData<V> Node;
  8. typedef HashTable<K, V, Hash, KeyOfV> Table;
  9. typedef __HashIterator<K, V, Hash, KeyOfV> Self;
  10. Node* _cur;
  11. Table* _ptable;
  12. //迭代器构造函数
  13. __HashIterator(Node* cur, Table* ptable)
  14. : _cur(cur)
  15. , _ptable(ptable)
  16. { }
  17. //迭代器自加函数
  18. Self& operator++()
  19. {
  20. //分两种情况讨论:cur所在的桶的next是否为空
  21. //_cur的下一个节点不为空
  22. if (_cur->_next)
  23. {
  24. _cur = _cur->_next;
  25. return *this;
  26. }
  27. else //_cur的_next节点为空,++要去到其他桶
  28. {
  29. Hash hash;
  30. KeyOfV kofv;
  31. size_t hashi = hash(kofv(_cur->_data)) % _ptable->_hashTable.size();
  32. for (size_t i = hashi + 1; i < _ptable->_hashTable.size(); ++i)
  33. {
  34. _cur = _ptable->_hashTable[i];
  35. if (_cur)
  36. {
  37. return *this;
  38. }
  39. }
  40. return *this;
  41. }
  42. }
  43. V& operator*()
  44. {
  45. return _cur->_data;
  46. }
  47. V* operator->()
  48. {
  49. return &_cur->_data;
  50. }
  51. //迭代器相等判断函数
  52. bool operator==(const Self& it)
  53. {
  54. return _cur == it._cur;
  55. }
  56. //迭代器不等判断函数
  57. bool operator!=(const Self& it)
  58. {
  59. return _cur != it._cur;
  60. }
  61. };

三. unordered_map和unordered_set的实现

在unordered_map和unordered_set中,会存储一个自定义类型HashTable的成员变量_ht,_ht是一张哈希表。unordered_map和unordered_set的insert、erase、find、begin、end()函数只需要复用HashTable的对应成员函数即可。

唯一需要注意的是unordered_map中的operator[]函数,其应当返回与Key匹配的Value的引用,或者插入一个键值为Key的数据。operator[]中复用哈希表insert函数,insert函数返回一键值对,这个键值对的first为原有Key的节点位置的迭代器或新插入节点的迭代器位置。

代码3.1:unordered_map的封装

  1. template<class K, class V, class Hash = HashFunc<K>>
  2. class unordered_map
  3. {
  4. //从节点数据中提取键值Key的仿函数
  5. struct MapKeyOfValue
  6. {
  7. const K& operator()(const std::pair<K, V>& kv)
  8. {
  9. return kv.first;
  10. }
  11. };
  12. public:
  13. typedef typename HashTable<K, std::pair<K, V>, Hash, MapKeyOfValue>::iterator iterator;
  14. iterator begin()
  15. {
  16. return _ht.begin();
  17. }
  18. iterator end()
  19. {
  20. return _ht.end();
  21. }
  22. std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
  23. {
  24. return _ht.insert(kv);
  25. }
  26. HashData<V>* find(const K& key)
  27. {
  28. return _ht.find(key);
  29. }
  30. bool erase(const K& key)
  31. {
  32. return _ht.find();
  33. }
  34. private:
  35. HashTable<K, std::pair<K, V>, Hash, MapKeyOfValue> _ht;
  36. };

代码3.2:unordered_set的封装

  1. template<class K, class Hash = HashFunc<K>>
  2. class unordered_set
  3. {
  4. struct SetKeyOfValue
  5. {
  6. const K& operator()(const K& data)
  7. {
  8. return data;
  9. }
  10. };
  11. public:
  12. typedef typename HashTable<K, K, Hash, SetKeyOfValue>::iterator iterator;
  13. iterator begin()
  14. {
  15. return _ht.begin();
  16. }
  17. iterator end()
  18. {
  19. return _ht.end();
  20. }
  21. std::pair<iterator, bool> insert(const K& data)
  22. {
  23. return _ht.insert(data);
  24. }
  25. iterator find(const K& data)
  26. {
  27. return _ht.find(data);
  28. }
  29. bool erase(const K& data)
  30. {
  31. return _ht.find(data);
  32. }
  33. private:
  34. HashTable<K, K, Hash, SetKeyOfValue> _ht;
  35. };

附录:哈希表封装unorder_map和unordered_set完整版代码

  1. //hashTable.h文件 -- 哈希表的实现
  2. #pragma once
  3. #include<vector>
  4. template<class V>
  5. struct HashData
  6. {
  7. V _data;
  8. HashData<V>* _next;
  9. HashData(const V& data)
  10. : _data(data)
  11. , _next(nullptr)
  12. { }
  13. };
  14. //仿函数 -- 用于将键值key转换为size_t类型数据
  15. //这样可以带入哈希函数,使不同key值可以映射到不同存储位置
  16. template<class K>
  17. struct HashFunc
  18. {
  19. size_t operator()(const K& key)
  20. {
  21. return (size_t)key;
  22. }
  23. };
  24. //哈希表类的声明
  25. template<class K, class V, class Hash, class KeyOfV>
  26. class HashTable;
  27. template<class K, class V, class Hash, class KeyOfV>
  28. struct __HashIterator
  29. {
  30. typedef HashData<V> Node;
  31. typedef HashTable<K, V, Hash, KeyOfV> Table;
  32. typedef __HashIterator<K, V, Hash, KeyOfV> Self;
  33. Node* _cur;
  34. Table* _ptable;
  35. //迭代器构造函数
  36. __HashIterator(Node* cur, Table* ptable)
  37. : _cur(cur)
  38. , _ptable(ptable)
  39. { }
  40. //迭代器自加函数
  41. Self& operator++()
  42. {
  43. //分两种情况讨论:cur所在的桶的next是否为空
  44. //_cur的下一个节点不为空
  45. if (_cur->_next)
  46. {
  47. _cur = _cur->_next;
  48. return *this;
  49. }
  50. else //_cur的_next节点为空,++要去到其他桶
  51. {
  52. Hash hash;
  53. KeyOfV kofv;
  54. size_t hashi = hash(kofv(_cur->_data)) % _ptable->_hashTable.size();
  55. for (size_t i = hashi + 1; i < _ptable->_hashTable.size(); ++i)
  56. {
  57. _cur = _ptable->_hashTable[i];
  58. if (_cur)
  59. {
  60. return *this;
  61. }
  62. }
  63. return *this;
  64. }
  65. }
  66. V& operator*()
  67. {
  68. return _cur->_data;
  69. }
  70. V* operator->()
  71. {
  72. return &_cur->_data;
  73. }
  74. //迭代器相等判断函数
  75. bool operator==(const Self& it)
  76. {
  77. return _cur == it._cur;
  78. }
  79. //迭代器不等判断函数
  80. bool operator!=(const Self& it)
  81. {
  82. return _cur != it._cur;
  83. }
  84. };
  85. template<class K, class V, class Hash, class KeyOfV>
  86. class HashTable
  87. {
  88. typedef HashData<V> Node;
  89. friend struct __HashIterator<K, V, Hash, KeyOfV>;
  90. public:
  91. typedef __HashIterator<K, V, Hash, KeyOfV> iterator;
  92. //哈希表析构函数
  93. ~HashTable()
  94. {
  95. //依次释放每个桶(单链表节点)
  96. for (size_t i = 0; i < _hashTable.size(); ++i)
  97. {
  98. Node* cur = _hashTable[i];
  99. //while循环释放每个非空节点
  100. while (cur)
  101. {
  102. Node* next = cur->_next;
  103. delete cur;
  104. cur = next;
  105. }
  106. _hashTable[i] = nullptr;
  107. }
  108. //_hashTable是vector成员,编译器会自动调用其析构函数
  109. }
  110. //查找哈希表第一个有效节点
  111. iterator begin()
  112. {
  113. for (size_t i = 0; i < _hashTable.size(); ++i)
  114. {
  115. if (_hashTable[i])
  116. {
  117. return iterator(_hashTable[i], this);
  118. }
  119. }
  120. return iterator(nullptr, this);
  121. }
  122. //最后一个节点的下一位置
  123. iterator end()
  124. {
  125. return iterator(nullptr, this);
  126. }
  127. //数据插入函数
  128. std::pair<iterator, bool> insert(const V& data)
  129. {
  130. //如果哈希表中已经存在kv,那么不插入数据,直接返回
  131. iterator ret = find(_kofv(data));
  132. if (ret != end())
  133. {
  134. return std::make_pair(ret, false);
  135. }
  136. //检查扩容
  137. //当哈希表容量为0(第一次插入数据前),或负载因子在插入数据后会大于1的情况下,对哈希表执行二倍扩容
  138. if (_hashTable.size() == 0 || _size == _hashTable.size())
  139. {
  140. size_t newSize = _hashTable.size() == 0 ? 10 : 2 * _hashTable.size(); //新容量
  141. //建立一个辅助新表(用于让扩容后原哈希表中数据映射到新的位置)
  142. std::vector<Node*> newTable;
  143. newTable.resize(newSize, nullptr); //表中每个哈希桶先初始化为nullptr
  144. //挪动数据 -- 等价于单链表头插操作
  145. for (size_t i = 0; i < _hashTable.size(); ++i)
  146. {
  147. Node* cur = _hashTable[i]; //当前哈希桶头结点
  148. //将原来的每个节点头插到新表的对应位置
  149. while (cur)
  150. {
  151. size_t hashi = _hash(_kofv(cur->_data)) % newSize;
  152. //头插
  153. Node* next = cur->_next;
  154. cur->_next = newTable[hashi];
  155. newTable[hashi] = cur;
  156. cur = next;
  157. }
  158. }
  159. _hashTable.swap(newTable);
  160. }
  161. Node* insertNode = new Node(data); //新插入的节点
  162. size_t hashi = _hash(_kofv(data)) % _hashTable.size(); //计算新节点在哪个哈希桶
  163. //节点插入
  164. insertNode->_next = _hashTable[hashi];
  165. _hashTable[hashi] = insertNode;
  166. ++_size;
  167. return std::make_pair(iterator(insertNode, this), true);
  168. }
  169. //数据查找函数
  170. iterator find(const K& key)
  171. {
  172. //表容量为0时,一定找不到任何数据
  173. //为了避免除0错误,直接返回
  174. if (_hashTable.size() == 0)
  175. {
  176. return iterator(nullptr, this);
  177. }
  178. size_t hashi = _hash(key) % _hashTable.size(); //确定key应该在哪个哈希桶
  179. Node* cur = _hashTable[hashi];
  180. //查找hashi对应的哈希桶是否存在key
  181. while (cur)
  182. {
  183. if (_kofv(cur->_data) == key)
  184. {
  185. return iterator(cur, this);
  186. }
  187. cur = cur->_next;
  188. }
  189. return iterator(nullptr, this);
  190. }
  191. //数据删除函数
  192. bool erase(const K& key)
  193. {
  194. //避免除0错误
  195. if (_hashTable.size() == 0)
  196. {
  197. return false;
  198. }
  199. size_t hashi = _hash(key) % _hashTable.size();
  200. Node* prev = nullptr;
  201. Node* cur = _hashTable[hashi];
  202. //找到键值为key的节点删除
  203. //等价于单链表节点删除操作 -- 分删除头结点和中间节点两种情况来讨论
  204. while (cur)
  205. {
  206. //找到了要删除的节点
  207. if (_kofv(cur->_data) == key)
  208. {
  209. //删除头结点
  210. if (prev == nullptr)
  211. {
  212. _hashTable[hashi] = cur->_next;
  213. free(cur);
  214. cur = nullptr;
  215. }
  216. else //删除中间节点
  217. {
  218. prev->_next = cur->_next;
  219. free(cur);
  220. cur = nullptr;
  221. }
  222. return true;
  223. }
  224. prev = cur;
  225. cur = cur->_next;
  226. }
  227. return false;
  228. }
  229. private:
  230. size_t _size = 0;
  231. Hash _hash;
  232. KeyOfV _kofv;
  233. std::vector<Node*> _hashTable;
  234. };
  235. //UnorderedMap.h文件 -- unordered_map的封装
  236. #pragma once
  237. #include "hashTable.h"
  238. namespace zhang
  239. {
  240. template<class K, class V, class Hash = HashFunc<K>>
  241. class unordered_map
  242. {
  243. //从节点数据中提取键值Key的仿函数
  244. struct MapKeyOfValue
  245. {
  246. const K& operator()(const std::pair<K, V>& kv)
  247. {
  248. return kv.first;
  249. }
  250. };
  251. public:
  252. typedef typename HashTable<K, std::pair<K, V>, Hash, MapKeyOfValue>::iterator iterator;
  253. iterator begin()
  254. {
  255. return _ht.begin();
  256. }
  257. iterator end()
  258. {
  259. return _ht.end();
  260. }
  261. std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
  262. {
  263. return _ht.insert(kv);
  264. }
  265. HashData<V>* find(const K& key)
  266. {
  267. return _ht.find(key);
  268. }
  269. bool erase(const K& key)
  270. {
  271. return _ht.find();
  272. }
  273. private:
  274. HashTable<K, std::pair<K, V>, Hash, MapKeyOfValue> _ht;
  275. };
  276. }
  277. //UnorderedSet.h文件 -- unordered_set的封装
  278. #pragma once
  279. #include "hashTable.h"
  280. namespace zhang
  281. {
  282. template<class K, class Hash = HashFunc<K>>
  283. class unordered_set
  284. {
  285. struct SetKeyOfValue
  286. {
  287. const K& operator()(const K& data)
  288. {
  289. return data;
  290. }
  291. };
  292. public:
  293. typedef typename HashTable<K, K, Hash, SetKeyOfValue>::iterator iterator;
  294. iterator begin()
  295. {
  296. return _ht.begin();
  297. }
  298. iterator end()
  299. {
  300. return _ht.end();
  301. }
  302. std::pair<iterator, bool> insert(const K& data)
  303. {
  304. return _ht.insert(data);
  305. }
  306. iterator find(const K& data)
  307. {
  308. return _ht.find(data);
  309. }
  310. bool erase(const K& data)
  311. {
  312. return _ht.find(data);
  313. }
  314. private:
  315. HashTable<K, K, Hash, SetKeyOfValue> _ht;
  316. };
  317. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/555627
推荐阅读
相关标签
  

闽ICP备14008679号