当前位置:   article > 正文

unordered_map和unordered_set模拟实现

unordered_map和unordered_set模拟实现

unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构。

一、哈希

1.1哈希概念

构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

1.2哈希冲突

不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。

1.3哈希函数

引起哈希冲突的一个原因可能是:哈希函数设计不够合理。

常见的哈希函数:

1.直接定址法:

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B

优点:简单、均匀

缺点:需要事先知道关键字的分布情况

使用场景:适合查找比较小且连续的情况

2.除留余数法:

设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址。

1.4哈希冲突解决

解决哈希冲突两种常见的方法是:闭散列和开散列

1.4.1闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。

1.线性探测

插入:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

删除:删除时不可以物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。因为在查找时,遇到空就查找结束。

线性探测的实现:

  1. template<class K>
  2. struct DefaultHashFunc
  3. {
  4. size_t operator()(const K& key)
  5. {
  6. return (size_t)key;
  7. }
  8. };
  9. template<>
  10. struct DefaultHashFunc<string>
  11. {
  12. size_t operator()(const string& str)
  13. {
  14. size_t n = 0;
  15. for (auto ch : str)
  16. {
  17. n *= 131;
  18. n += ch;
  19. }
  20. return n;
  21. }
  22. };
  23. namespace open_address//开放地址法
  24. {
  25. //状态
  26. enum STATE
  27. {
  28. EMPTY, EXIST, DELETE//空,存在,删除
  29. };
  30. //数据类型
  31. template<class K, class V>
  32. struct HashData
  33. {
  34. pair<K, V> _kv;
  35. STATE _state = EMPTY;
  36. };
  37. template<class K, class V, class HashFunc = DefaultHashFunc<K>>
  38. class HashTable
  39. {
  40. public:
  41. HashTable()
  42. {
  43. _table.resize(10);
  44. }
  45. bool insert(const pair<K, V>& kv)
  46. {
  47. //检查是否已经存在
  48. if (Find(kv.first))
  49. return false;
  50. //判断扩容
  51. HashFunc ht;
  52. int fac = (double)_n / _table.size();//负载因子
  53. if (fac >= 0.7)
  54. {
  55. //扩容之后可能会改变数据的映射关系,所以不能直接扩容
  56. size_t newcapacity = _table.capacity() * 2;
  57. HashTable<K, V> newHash;
  58. newHash._table.resize(newcapacity);
  59. //遍历旧表,插入到新表
  60. for (int i = 0; i < _table.capacity(); i++)
  61. {
  62. //只有exist状态的才插入新表
  63. if (_table[i]._state == EXIST)
  64. newHash.insert(_table[i]._kv);
  65. }
  66. _table.swap(newHash._table);
  67. }
  68. //插入
  69. size_t hashi = ht(kv.first) % _table.size();
  70. while (_table[hashi]._state == EXIST)
  71. {
  72. hashi++;
  73. if (hashi == _table.size())//回到0位置
  74. hashi = 0;
  75. }
  76. _table[hashi]._kv = kv;
  77. _table[hashi]._state = EXIST;
  78. _n++;
  79. return true;
  80. }
  81. HashData<const K, V>* Find(const K& key)
  82. {
  83. HashFunc ht;
  84. size_t hashi = ht(key) % _table.size();
  85. while (_table[hashi]._state != EMPTY)
  86. {
  87. if (_table[hashi]._state != DELETE && _table[hashi]._kv.first == key)
  88. return (HashData<const K, V>*) & _table[hashi];
  89. hashi++;
  90. //转回去继续找
  91. if (hashi == _table.size())//回到0位置
  92. hashi = 0;
  93. }
  94. return nullptr;
  95. }
  96. bool Erase(const K& key)
  97. {
  98. HashData<const K, V>* data = Find(key);
  99. if (data)
  100. {
  101. data->_state = DELETE;
  102. _n--;
  103. return true;
  104. }
  105. return false;
  106. }
  107. private:
  108. vector<HashData<K, V>> _table;
  109. size_t _n = 0;//存储数据的有效个数
  110. };
  111. }

哈希表什么情况下扩容?如何扩容?

2.二次探测

线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:

若当前key与原来key产生相同的哈希地址,则当前key存在该地址后偏移量为(1,-1,2,-2,3,-3...)的二次方地址处
key1:hash(key)+0

key2:hash(key)+1^2

key3:hash(key)+(-1)^2

key4:hash(key)+2^2

key5:hash(key)+(-2)^2

1.4.2开散列

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

开散列的实现:

  1. template<class K>
  2. struct DefaultHashFunc
  3. {
  4. size_t operator()(const K& key)
  5. {
  6. return (size_t)key;
  7. }
  8. };
  9. template<>
  10. struct DefaultHashFunc<string>
  11. {
  12. size_t operator()(const string& str)
  13. {
  14. size_t n = 0;
  15. for (auto ch : str)
  16. {
  17. n *= 131;
  18. n += ch;
  19. }
  20. return n;
  21. }
  22. };
  23. namespace hash_bucket//拉链法——哈希桶
  24. {
  25. template<class K, class V>
  26. struct HashNode
  27. {
  28. HashNode(const pair<K, V>& kv)
  29. :_kv(kv)
  30. ,_next(nullptr)
  31. {}
  32. pair<K, V> _kv;
  33. HashNode<K, V>* _next;
  34. };
  35. template<class K, class V, class HashFunc = DefaultHashFunc<K>>
  36. class HashTable
  37. {
  38. typedef HashNode<K, V> Node;
  39. public:
  40. HashTable()
  41. {
  42. _table.resize(10, nullptr);
  43. }
  44. ~HashTable()
  45. {
  46. for (int i = 0; i < _table.size(); i++)
  47. {
  48. Node* cur = _table[i];
  49. while (cur)
  50. {
  51. Node* next = cur->_next;
  52. delete cur;
  53. cur = next;
  54. }
  55. _table[i] = nullptr;
  56. }
  57. _n = 0;
  58. }
  59. bool insert(const pair<K, V>& kv)
  60. {
  61. //检查是否已经存在
  62. if (Find(kv.first))
  63. return false;
  64. HashFunc ht;
  65. size_t fac = (double)_n / _table.size();//负载因子
  66. if (fac >= 1)//扩容
  67. {
  68. size_t newcapacity = _table.size() * 2;
  69. HashTable<K, V> newht;
  70. newht._table.resize(newcapacity, nullptr);
  71. //遍历旧的,链入新的
  72. for (int i = 0; i < _table.size(); i++)
  73. {
  74. Node* cur = _table[i];
  75. while (cur)
  76. {
  77. Node* next = cur->_next;
  78. cur->_next = nullptr;//如果用屏蔽的方式,这里要置空
  79. size_t hashi = ht(cur->_kv.first) % newcapacity;
  80. //头插
  81. cur->_next = newht._table[hashi];
  82. newht._table[hashi] = cur;
  83. /*if (newht._table[hashi] == nullptr)
  84. {
  85. newht._table[hashi] = cur;
  86. }
  87. else
  88. {
  89. cur->_next = newht._table[hashi];
  90. newht._table[hashi] = cur;
  91. }*/
  92. cur = next;
  93. }
  94. _table[i] = nullptr;//这里必须要置空,否则不能释放
  95. }
  96. _table.swap(newht._table);
  97. }
  98. //头插
  99. Node* newnode = new Node(kv);
  100. size_t hashi = ht(kv.first) % _table.size();//找第几个桶
  101. newnode->_next = _table[hashi];
  102. _table[hashi] = newnode;
  103. /*if (_table[hashi] == nullptr)
  104. {
  105. _table[hashi] = newnode;
  106. }
  107. else
  108. {
  109. newnode->_next = _table[hashi];
  110. _table[hashi] = newnode;
  111. }*/
  112. _n++;
  113. return true;
  114. }
  115. Node* Find(const K& key)
  116. {
  117. HashFunc ht;
  118. size_t hashi = ht(key) % _table.size();
  119. Node* cur = _table[hashi];
  120. while (cur)
  121. {
  122. if (cur->_kv.first == key)
  123. {
  124. return cur;
  125. }
  126. cur = cur->_next;
  127. }
  128. return nullptr;
  129. }
  130. bool Erase(const K& key)
  131. {
  132. HashFunc ht;
  133. size_t hashi = ht(key) % _table.size();
  134. Node* prev = nullptr;
  135. Node* cur = _table[hashi];
  136. while (cur)
  137. {
  138. if (cur->_kv.first == key)
  139. {
  140. if (prev == nullptr)
  141. {
  142. _table[hashi] = cur->_next;
  143. }
  144. else
  145. {
  146. prev->_next = cur->_next;
  147. }
  148. delete cur;
  149. return true;
  150. }
  151. else
  152. {
  153. prev = cur;
  154. cur = cur->_next;
  155. }
  156. }
  157. return false;
  158. }
  159. void Print()
  160. {
  161. for (int i = 0; i < _table.size(); i++)
  162. {
  163. Node* cur = _table[i];
  164. printf("[%d] : ", i);
  165. while (cur)
  166. {
  167. cout << "(" << cur->_kv.first << ":" << cur->_kv.second << ")" << "->";
  168. cur = cur->_next;
  169. }
  170. cout << "NULL" << endl;
  171. }
  172. cout << endl;
  173. }
  174. private:
  175. vector<Node*> _table;
  176. size_t _n = 0;//存放有效数据的个数
  177. };
  178. }

注意:如果我们要存放的数据是整型,可以使用上面的哈希函数来映射,但是如果我们存的是字符串或自定义类型的数据时,问题就来了,我们如何用除留余数法来计算呢?字符串和自定义类型不可以取模。

我们可以使用仿函数,如果是整型就返回整型,如果是字符串或自定义类型,就将其转换成整型再返回:

  1. template<class K>
  2. struct DefaultHashFunc
  3. {
  4. size_t operator()(const K& key)
  5. {
  6. return (size_t)key;
  7. }
  8. };
  9. template<>
  10. struct DefaultHashFunc<string>
  11. {
  12. size_t operator()(const string& str)
  13. {
  14. size_t n = 0;
  15. for (auto ch : str)
  16. {
  17. n *= 131;
  18. n += ch;
  19. }
  20. return n;
  21. }
  22. };

二、unordered_map和unordered_set的模拟实现

底层哈希桶:

  1. template<class K>
  2. struct DefaultHashFunc
  3. {
  4. size_t operator()(const K& key)
  5. {
  6. return (size_t)key;
  7. }
  8. };
  9. template<>
  10. struct DefaultHashFunc<string>
  11. {
  12. size_t operator()(const string& str)
  13. {
  14. size_t n = 0;
  15. for (auto ch : str)
  16. {
  17. n *= 131;
  18. n += ch;
  19. }
  20. return n;
  21. }
  22. };
  23. namespace hash_bucket//拉链法——哈希桶
  24. {
  25. template<class T>
  26. struct HashNode
  27. {
  28. HashNode(const T& data)
  29. :_data(data)
  30. ,_next(nullptr)
  31. {}
  32. T _data;
  33. HashNode<T>* _next;
  34. };
  35. template<class K, class T, class KofT, class HashFunc>
  36. class HashTable;//声明一下
  37. template<class K, class T, class Ref, class Ptr, class KofT, class HashFunc = DefaultHashFunc<K>>
  38. struct __HT_Iterator
  39. {
  40. typedef HashNode<T> Node;
  41. typedef __HT_Iterator<K, T, Ref, Ptr, KofT, HashFunc> Self;
  42. typedef __HT_Iterator<K, T, T&, T*, KofT, HashFunc> Iterator;
  43. __HT_Iterator(Node* node, const HashTable<K, T, KofT, HashFunc>* pht)
  44. :_node(node)
  45. ,_pht(pht)//_pht为const修饰的指针
  46. {}
  47. //当此时这个类是普通迭代器类时,这个函数为拷贝构造
  48. //当此时这个类是const迭代器类时,这个函数为构造
  49. __HT_Iterator(const Iterator& it)
  50. :_node(it._node)
  51. ,_pht(it._pht)
  52. {}
  53. Ref operator*()
  54. {
  55. return _node->_data;
  56. }
  57. Ptr operator->()
  58. {
  59. return &_node->_data;
  60. }
  61. Self operator++()
  62. {
  63. //走这个桶里的下一个节点
  64. if (_node->_next)
  65. {
  66. _node = _node->_next;
  67. }
  68. //找其他桶
  69. else
  70. {
  71. KofT kot;
  72. HashFunc ht;
  73. size_t hashi = ht(kot(_node->_data)) % _pht->_table.size();
  74. ++hashi;//找下一个桶
  75. while (hashi < _pht->_table.size())
  76. {
  77. if (_pht->_table[hashi])
  78. {
  79. _node = _pht->_table[hashi];
  80. return *this;
  81. }
  82. else
  83. {
  84. ++hashi;
  85. }
  86. }
  87. _node = nullptr;
  88. }
  89. return *this;
  90. }
  91. bool operator!=(const Self& it) const
  92. {
  93. return _node != it._node;
  94. }
  95. bool operator==(const Self& it) const
  96. {
  97. return _node == it._node;
  98. }
  99. Node* _node;
  100. const HashTable<K, T, KofT, HashFunc>* _pht;
  101. };
  102. template<class K, class T, class KofT, class HashFunc = DefaultHashFunc<K>>
  103. class HashTable
  104. {
  105. //友元声明
  106. template<class K, class T, class Ref, class Ptr, class KofT, class HashFunc>
  107. friend struct __HT_Iterator;
  108. typedef HashNode<T> Node;
  109. public:
  110. typedef __HT_Iterator<K, T, T&, T*, KofT, HashFunc> iterator;
  111. typedef __HT_Iterator<K, T, const T&, const T*, KofT, HashFunc> const_iterator;
  112. iterator begin()
  113. {
  114. for (int i = 0; i < _table.size(); i++)
  115. {
  116. Node* cur = _table[i];
  117. if (cur)
  118. return iterator(cur, this);
  119. }
  120. return iterator(nullptr, this);
  121. }
  122. iterator end()
  123. {
  124. return iterator(nullptr, this);
  125. }
  126. const_iterator begin() const
  127. {
  128. for (int i = 0; i < _table.size(); i++)
  129. {
  130. Node* cur = _table[i];
  131. if (cur)
  132. return const_iterator(cur, this);
  133. }
  134. return const_iterator(nullptr, this);
  135. }
  136. const_iterator end() const
  137. {
  138. return const_iterator(nullptr, this);//这里的this是const修饰的,所以上面的迭代器里的构造的哈希表参数要设置成const
  139. }
  140. HashTable()
  141. {
  142. _table.resize(10, nullptr);
  143. }
  144. ~HashTable()
  145. {
  146. for (int i = 0; i < _table.size(); i++)
  147. {
  148. Node* cur = _table[i];
  149. while (cur)
  150. {
  151. Node* next = cur->_next;
  152. delete cur;
  153. cur = next;
  154. }
  155. _table[i] = nullptr;
  156. }
  157. _n = 0;
  158. }
  159. pair<iterator, bool> insert(const T& data)
  160. {
  161. KofT kot;
  162. //检查是否已经存在
  163. iterator it = Find(kot(data));
  164. if (it != end())//找到了就返回此刻的pair
  165. return make_pair(it ,false);
  166. HashFunc ht;
  167. size_t fac = (double)_n / _table.size();//负载因子
  168. if (fac >= 1)//扩容
  169. {
  170. size_t newcapacity = _table.size() * 2;
  171. HashTable<K, T, KofT> newht;
  172. newht._table.resize(newcapacity, nullptr);
  173. //遍历旧的,链入新的
  174. for (int i = 0; i < _table.size(); i++)
  175. {
  176. Node* cur = _table[i];
  177. while (cur)
  178. {
  179. Node* next = cur->_next;
  180. size_t hashi = ht(kot(cur->_data)) % newcapacity;
  181. //头插
  182. cur->_next = newht._table[hashi];
  183. newht._table[hashi] = cur;
  184. cur = next;
  185. }
  186. _table[i] = nullptr;//这里必须要置空,否则不能释放
  187. }
  188. _table.swap(newht._table);
  189. }
  190. //头插
  191. Node* newnode = new Node(data);
  192. size_t hashi = ht(kot(data)) % _table.size();//找第几个桶
  193. newnode->_next = _table[hashi];
  194. _table[hashi] = newnode;
  195. _n++;
  196. return make_pair(iterator(newnode, this), true);
  197. }
  198. iterator Find(const K& key)
  199. {
  200. KofT kot;
  201. HashFunc ht;
  202. size_t hashi = ht(key) % _table.size();
  203. Node* cur = _table[hashi];
  204. while (cur)
  205. {
  206. if (kot(cur->_data) == key)
  207. {
  208. return iterator(cur, this);
  209. }
  210. cur = cur->_next;
  211. }
  212. return end();
  213. }
  214. bool Erase(const K& key)
  215. {
  216. KofT kot;
  217. HashFunc ht;
  218. size_t hashi = ht(key) % _table.size();
  219. Node* prev = nullptr;
  220. Node* cur = _table[hashi];
  221. while (cur)
  222. {
  223. if (kot(cur->_data) == key)
  224. {
  225. if (prev == nullptr)
  226. {
  227. _table[hashi] = cur->_next;
  228. }
  229. else
  230. {
  231. prev->_next = cur->_next;
  232. }
  233. delete cur;
  234. _n--;
  235. return true;
  236. }
  237. else
  238. {
  239. prev = cur;
  240. cur = cur->_next;
  241. }
  242. }
  243. return false;
  244. }
  245. void Print()
  246. {
  247. for (int i = 0; i < _table.size(); i++)
  248. {
  249. Node* cur = _table[i];
  250. printf("[%d] : ", i);
  251. while (cur)
  252. {
  253. cout << "(" << cur->_kv.first << ":" << cur->_kv.second << ")" << "->";
  254. cur = cur->_next;
  255. }
  256. cout << "NULL" << endl;
  257. }
  258. cout << endl;
  259. }
  260. size_t count(const K& key)
  261. {
  262. if (Find(key) != end())
  263. return 1;
  264. else
  265. return 0;
  266. }
  267. size_t size()
  268. {
  269. return _n;
  270. }
  271. bool empty()
  272. {
  273. return _n == 0;
  274. }
  275. private:
  276. vector<Node*> _table;
  277. size_t _n = 0;//存放有效数据的个数
  278. };
  279. }

封装unordered_map和unordered_set

  1. template<class K, class V>
  2. struct mapKofT
  3. {
  4. K operator()(const pair<K, V>& kv)
  5. {
  6. return kv.first;
  7. }
  8. };
  9. template<class K, class V>
  10. class Unordered_map
  11. {
  12. public:
  13. typedef typename hash_bucket::HashTable<K, pair<const K, V>, mapKofT<K, V>>::iterator iterator;
  14. typedef typename hash_bucket::HashTable<K, pair<const K, V>, mapKofT<K, V>>::const_iterator const_iterator;
  15. pair<iterator, bool> insert(const pair<K, V>& kv)
  16. {
  17. return _ht.insert(kv);
  18. }
  19. V& operator[](const K& key)
  20. {
  21. return _ht.insert(make_pair(key, V())).first->second;
  22. }
  23. bool Erase(const K& key)
  24. {
  25. return _ht.Erase(key);
  26. }
  27. iterator find(const K& key)
  28. {
  29. return _ht.Find(key);
  30. }
  31. size_t count(const K& key)
  32. {
  33. return _ht.count(key);
  34. }
  35. size_t size()
  36. {
  37. return _ht.size();
  38. }
  39. bool empty()
  40. {
  41. return _ht.empty();
  42. }
  43. iterator begin()
  44. {
  45. return _ht.begin();
  46. }
  47. iterator end()
  48. {
  49. return _ht.end();
  50. }
  51. const_iterator begin() const
  52. {
  53. return _ht.begin();
  54. }
  55. const_iterator end() const
  56. {
  57. return _ht.end();
  58. }
  59. private:
  60. hash_bucket::HashTable<K, pair<const K, V>, mapKofT<K, V>> _ht;
  61. };
  1. template<class K>
  2. struct setKofT
  3. {
  4. K operator()(const K& key)
  5. {
  6. return key;
  7. }
  8. };
  9. template<class K>
  10. class Unordered_set
  11. {
  12. public:
  13. typedef typename hash_bucket::HashTable<K, K, setKofT<K>>::const_iterator iterator;
  14. typedef typename hash_bucket::HashTable<K, K, setKofT<K>>::const_iterator const_iterator;
  15. pair<iterator, bool> insert(const K& key)
  16. {
  17. return _ht.insert(key);
  18. }
  19. bool Erase(const K& key)
  20. {
  21. return _ht.Erase(key);
  22. }
  23. size_t size()
  24. {
  25. return _ht.size();
  26. }
  27. bool empty()
  28. {
  29. return _ht.empty();
  30. }
  31. size_t count(const K& key)
  32. {
  33. return _ht.count(key);
  34. }
  35. iterator find(const K& key)
  36. {
  37. return _ht.Find(key);
  38. }
  39. iterator begin() const
  40. {
  41. return _ht.begin();
  42. }
  43. iterator end() const
  44. {
  45. return _ht.end();
  46. }
  47. private:
  48. hash_bucket::HashTable<K, K, setKofT<K>> _ht;
  49. };
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/933034?site
推荐阅读
相关标签
  

闽ICP备14008679号