当前位置:   article > 正文

【C++】哈希

【C++】哈希

1. unordered系列关联式容器

STL提供了底层为红黑树结构的一系列关联式容

这里介绍 unordered_set 和 unordered_map

a. unordered_map

  1. unordered_map 是存储<key, value>键值对的关联式容器,其允许通过 key 快速的索引到与

其对应的 value

  1. unordered_map 容器通过 key 访问单个元素要比 map 快,但它通常在遍历元素子集的范围迭

代方面效率较低

  1. unordered_map 实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value
  2. 它的迭代器至少是前向迭代器
  3. 重复数据可以去重

b. unordered_set

  1. unordered_set 允许通过 key 快速的索引是否存在
  2. 重复数据可以去重
  3. 删除,查找,插入,效率都很快

2. unordered_map 的接口说明

a. unordered_map 的容量

函数声明

1. bool empty() const

2. size_t size() const

功能介绍

1. 检测unordered_map是否为空

2. 获取unordered_map的有效元素个数

b. unordered_map 的迭代器

函数声明

1. iterator begin()

2. iterator end()

3. iterator cbegin() const

4. iterator cend() const

功能介绍

1. 返回 unordered_map 第一个元素的迭代器

2. 返回 unordered_map 最后一个元素下一个位置的迭代器

3. 返回 unordered_map 第一个元素的const迭代器

4. 返回 unordered_map 最后一个元素下一个位置的const迭代器

c. unordered_map 的元素访问

函数声明

1. K& operator[]

功能介绍

1. 返回与 key 对应的 value ,允许被修改

d. unordered_map 的查询

函数声明

1. iterator find(const K& key)

2. size_t count(const K& key)

功能介绍

1. 返回key在哈希桶中的位置

2. 返回哈希桶中关键码为key的键值对的个数

注意:

unordered_map 中 key 是不能重复的,因此 count函数的返回值最大为 1

e. unordered_map 的桶操作

函数声明

1. size_t bucket_count() const

2. size_t bucket_size(size_t n) const

3. size_t bucket(const K& key)

功能介绍

1. 返回哈希桶中桶的总个数

2. 返回n号桶中有效元素的总个数

3. 返回元素key所在的桶号

注意:

unordered_set 用法差不多,就不介绍了

3. 哈希概念

通过位置关系找到对应的 key

a. 哈希冲突解决

先说上述问题的解决:

设插入的值为 key,n 为数组的大小

hashi (数组下标) = key % n (若 key 不为整数, 另做处理)

但是在插入的过程中,我们容易遇到以下问题:

  1. hashi 已经有数值存入 (这种问题又叫 哈希冲突

第一种方法:(闭散列

如果是这种情况,直接往后找,直到遇到空位置 (数组里面存入什么值都很难表示这个位置的状态,所以我们自己可以加入)

     2. 所有的位置都有没有位置了

这种问题一定要涉及到空间的开辟 (但是这里我们需要谈论的是什么时候扩空间比较好)

注意:

这种分情况,后面实现再讲

另外一种方法解决(开散列):

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地

址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链

接起来,各链表的头结点存储在哈希表中

4. 闭散列实现

代码

 

  1. enum State
  2. {
  3. Empty,
  4. Exist,
  5. Delete
  6. };
  7. template<class K,class V>
  8. struct HashTable
  9. {
  10. pair<K,V> _table;
  11. State _state = Empty;
  12. };
  13. template<class K, class V>
  14. class Hsah
  15. {
  16. public:
  17. bool insert(const pair<K, V>& kv)
  18. {
  19. if (_t.size() == 0 || n * 10 / _t.size() >= 7)
  20. {
  21. Hsah<K, V> x;
  22. size_t size = _t.size() == 0 ? 10 : _t.size() * 2;
  23. x._t.resize(size);
  24. for (auto i : _t)
  25. {
  26. if (i._state == Exist)
  27. {
  28. x.insert(i._table);
  29. }
  30. }
  31. _t.swap(x._t);
  32. }
  33. size_t hashi = kv.first % _t.size();
  34. int index = hashi;
  35. size_t i = 1;
  36. while (_t[index]._state != Empty)
  37. {
  38. index = hashi + i;
  39. index %= _t.size();
  40. i++;
  41. }
  42. _t[index]._table = kv;
  43. _t[index]._state = Exist;
  44. n++;
  45. return true;
  46. }
  47. HashTable<K, V>* find(const K& key)
  48. {
  49. if (_t.size() == 0)
  50. {
  51. return nullptr;
  52. }
  53. size_t hashi = key % _t.size();
  54. int index = hashi;
  55. size_t i = 1;
  56. while (_t[index]._state != Empty)
  57. {
  58. if (_t[index]._table.first == key)
  59. {
  60. if (_t[index]._state == Delete)
  61. {
  62. return nullptr;
  63. }
  64. return &_t[index];
  65. }
  66. index = hashi + i;
  67. index %= _t.size();
  68. i++;
  69. if (index == hashi)
  70. {
  71. break;
  72. }
  73. }
  74. return nullptr;
  75. }
  76. bool Erase(const K& key)
  77. {
  78. HashTable<K, V>* t = find(key);
  79. if (t == nullptr || t->_state == Delete)
  80. {
  81. return false;
  82. }
  83. t->_state = Delete;
  84. }
  85. private:
  86. size_t n = 0;
  87. vector<HashTable<K,V>> _t;
  88. };

5. 开散列实现

代码

  1. template<class K, class V>
  2. struct HashNode
  3. {
  4. HashNode(const pair<K, V>& kv)
  5. :_kv(kv)
  6. ,next(nullptr)
  7. {}
  8. HashNode<K, V>* next;
  9. pair<K, V> _kv;
  10. };
  11. template<class K, class V>
  12. class HashBucket
  13. {
  14. public:
  15. typedef HashNode<K, V> Node;
  16. void insert(const pair<K, V>& kv)
  17. {
  18. if (n == _hash.size())
  19. {
  20. size_t newsize = _hash.size() == 0 ? 10 : _hash.size() * 2;
  21. vector<Node*> newnode(newsize, nullptr);
  22. for (auto cur : _hash)
  23. {
  24. while (cur)
  25. {
  26. int hashi = cur->_kv.first % newsize;
  27. Node* prev = newnode[hashi];
  28. newnode[hashi] = cur;
  29. cur->next = prev;
  30. cur = cur->next;
  31. }
  32. }
  33. _hash.swap(newnode);
  34. }
  35. int hashi = kv.first % _hash.size();
  36. Node* cur = new Node(kv);
  37. Node* _next = _hash[hashi];
  38. _hash[hashi] = cur;
  39. cur->next = _next;
  40. n++;
  41. }
  42. bool find(const K& key)
  43. {
  44. if (_hash.size() == 0)
  45. {
  46. return false;
  47. }
  48. int hashi = key % _hash.size();
  49. Node* cur = _hash[hashi];
  50. while (cur)
  51. {
  52. if (cur->_kv.first = key)
  53. {
  54. return true;
  55. }
  56. cur = cur->next;
  57. }
  58. return false;
  59. }
  60. Node* erase(const K& key)
  61. {
  62. int hashi = key % _hash.size();
  63. Node* prev = nullptr;
  64. Node* cur = _hash[hashi];
  65. while (cur)
  66. {
  67. if (cur->_kv.first = key)
  68. {
  69. if (prev == nullptr)
  70. {
  71. _hash[hashi] = cur->next;
  72. }
  73. else
  74. {
  75. prev->next = cur->next;
  76. }
  77. delete cur;
  78. break;
  79. }
  80. prev = cur;
  81. cur = cur->next;
  82. }
  83. return nullptr;
  84. }
  85. private:
  86. size_t n = 0;
  87. vector<Node*> _hash;
  88. };

//这里插入选择了头插

6. unordered_map , unordered_set 底层实现

代码

“test.h” :

  1. #pragma once
  2. template<class K>
  3. struct Compare
  4. {
  5. size_t operator()(const K& key)
  6. {
  7. return key;
  8. }
  9. };
  10. template<>
  11. struct Compare<string>
  12. {
  13. size_t operator()(const string& k)
  14. {
  15. size_t x = 0;
  16. for (auto i : k)
  17. {
  18. x += i;
  19. x *= 31;
  20. }
  21. return x;
  22. }
  23. };
  24. namespace unordered
  25. {
  26. template<class K, class T>
  27. struct HashNode
  28. {
  29. HashNode(const T& data)
  30. :_data(data)
  31. , next(nullptr)
  32. {}
  33. HashNode<K, T>* next;
  34. T _data;
  35. };
  36. template<class K, class T, class Key0f, class compare>
  37. class HashBucket;
  38. template<class K, class T,class Ref,class Ptr, class Key0f, class compare>
  39. struct HashIterator
  40. {
  41. typedef typename HashIterator<K, T,Ref,Ptr, Key0f, compare> Self;
  42. typedef HashNode<K, T> Node;
  43. HashIterator(Node* hash,const HashBucket<K, T, Key0f, compare>* x)
  44. :_node(hash)
  45. ,p(x)
  46. {}
  47. Self& operator++()
  48. {
  49. if (_node->next)
  50. {
  51. _node = _node->next;
  52. }
  53. else
  54. {
  55. Key0f kf;
  56. int hashi = compare()(kf(_node->_data)) % (p->_hash.size());
  57. ++hashi;
  58. while (hashi < p->_hash.size())
  59. {
  60. if (p->_hash[hashi])
  61. {
  62. _node = p->_hash[hashi];
  63. break;
  64. }
  65. else
  66. {
  67. ++hashi;
  68. }
  69. }
  70. if (hashi == p->_hash.size())
  71. {
  72. _node = nullptr;
  73. }
  74. }
  75. return *this;
  76. }
  77. Ref operator*()
  78. {
  79. return _node->_data;
  80. }
  81. Ptr operator->()
  82. {
  83. return &_node->_data;
  84. }
  85. bool operator!=(const Self& ptr)
  86. {
  87. return _node != ptr._node;
  88. }
  89. const HashBucket<K, T, Key0f, compare>* p;
  90. Node* _node;
  91. };
  92. template<class K, class T, class Key0f, class compare>
  93. class HashBucket
  94. {
  95. template <class K, class T,class Ref,class Ptr, class Key0f, class compare>
  96. friend class HashIterator;
  97. public:
  98. typedef HashNode<K, T> Node;
  99. typedef typename HashBucket<K, T, Key0f, compare> Self;
  100. typedef typename HashIterator<K, T,T& ,T* ,Key0f, compare> Iterator;
  101. typedef typename HashIterator<K,T,const T&, const T*, Key0f, compare> const_Iterator;
  102. Iterator begin()
  103. {
  104. for (int i = 0; i < _hash.size(); i++)
  105. {
  106. if (_hash[i])
  107. {
  108. return Iterator(_hash[i], this);
  109. }
  110. }
  111. return end();
  112. }
  113. const_Iterator begin() const
  114. {
  115. for (int i = 0; i < _hash.size(); i++)
  116. {
  117. if (_hash[i])
  118. {
  119. return const_Iterator(_hash[i], this);
  120. }
  121. }
  122. return end();
  123. }
  124. Iterator end()
  125. {
  126. return Iterator(nullptr, this);
  127. }
  128. const_Iterator end() const
  129. {
  130. return const_Iterator(nullptr, this);
  131. }
  132. pair<Iterator,bool> insert(const T& data)
  133. {
  134. Key0f kf;
  135. Iterator it = find(kf(data));
  136. if (it != Iterator(nullptr,this))
  137. {
  138. return make_pair(it, false);
  139. }
  140. if (n == _hash.size())
  141. {
  142. size_t newsize = _hash.size() == 0 ? 10 : _hash.size() * 2;
  143. vector<Node*> newnode(newsize, nullptr);
  144. for (auto cur : _hash)
  145. {
  146. while (cur)
  147. {
  148. int hashi = compare()(kf(cur->_data)) % newsize;
  149. Node* prev = newnode[hashi];
  150. newnode[hashi] = cur;
  151. cur->next = prev;
  152. cur = cur->next;
  153. }
  154. }
  155. _hash.swap(newnode);
  156. }
  157. int hashi = compare()(kf(data)) % _hash.size();
  158. Node* cur = new Node(data);
  159. Node* _next = _hash[hashi];
  160. _hash[hashi] = cur;
  161. cur->next = _next;
  162. n++;
  163. return make_pair(Iterator(cur,this), true);
  164. }
  165. Iterator find(const K& key)
  166. {
  167. Key0f kf;
  168. if (_hash.size() == 0)
  169. {
  170. return Iterator(nullptr,this);
  171. }
  172. int hashi = compare()(key) % _hash.size();
  173. Node* cur = _hash[hashi];
  174. while (cur)
  175. {
  176. if (kf(cur->_data) == key)
  177. {
  178. return Iterator(cur, this);
  179. }
  180. cur = cur->next;
  181. }
  182. return Iterator(nullptr, this);
  183. }
  184. T* erase(const K& key)
  185. {
  186. Key0f kf;
  187. int hashi = compare()(key) % _hash.size();
  188. Node* prev = nullptr;
  189. Node* cur = _hash[hashi];
  190. while (cur)
  191. {
  192. if (kf(cur->_data) == key)
  193. {
  194. if (prev == nullptr)
  195. {
  196. _hash[hashi] = cur->next;
  197. }
  198. else
  199. {
  200. prev->next = cur->next;
  201. }
  202. delete cur;
  203. break;
  204. }
  205. prev = cur;
  206. cur = cur->next;
  207. }
  208. return nullptr;
  209. }
  210. private:
  211. size_t n = 0;
  212. vector<Node*> _hash;
  213. };
  214. }

 

 "my_map.h" :

  1. #pragma once
  2. #include "test.h"
  3. template<class K,class V,class Hash = Compare<K>>
  4. class map
  5. {
  6. struct MapOf
  7. {
  8. const K& operator()(const pair<K,V>& _key)
  9. {
  10. return _key.first;
  11. }
  12. };
  13. public:
  14. typedef typename unordered::HashBucket<K, pair<const K, V>, MapOf, Hash>::const_Iterator Iterator;
  15. typedef typename unordered::HashBucket<K, pair<const K, V>, MapOf, Hash>::const_Iterator const_Iterator;
  16. pair<const_Iterator, bool> insert(const pair<K, V>& data)
  17. {
  18. return pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);
  19. }
  20. Iterator find(const K& data)
  21. {
  22. return key.find(data);
  23. }
  24. pair<K, V>* erase(const K& data)
  25. {
  26. return key.erase(data);
  27. }
  28. const_Iterator begin() const
  29. {
  30. return key.begin();
  31. }
  32. const_Iterator end() const
  33. {
  34. return key.end();
  35. }
  36. V& operator[](const K& k)
  37. {
  38. pair<const_Iterator, bool> ret = key.insert(make_pair(k,V()));
  39. return ret.first->second;
  40. }
  41. private:
  42. unordered::HashBucket<K, pair<const K, V>, MapOf,Hash> key;
  43. };

 

 "my_set.h" :

  1. #pragma once
  2. #include "test.h"
  3. template<class K,class Hash = Compare<K>>
  4. class set
  5. {
  6. struct SetOf
  7. {
  8. const K& operator()(const K& _key)
  9. {
  10. return _key;
  11. }
  12. };
  13. public:
  14. typedef typename unordered::HashBucket<K, const K ,SetOf, Hash>::const_Iterator Iterator;
  15. typedef typename unordered::HashBucket<K, const K, SetOf, Hash>::const_Iterator const_Iterator;
  16. pair<const_Iterator,bool> insert(const K& data)
  17. {
  18. return pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);
  19. }
  20. Iterator find(const K& data)
  21. {
  22. return key.find(data);
  23. }
  24. const K* erase(const K& data)
  25. {
  26. return key.erase(data);
  27. }
  28. const_Iterator begin() const
  29. {
  30. return key.begin();
  31. }
  32. const_Iterator end() const
  33. {
  34. return key.end();
  35. }
  36. private:
  37. unordered::HashBucket<K, const K, SetOf,Hash> key;
  38. };

 

 

仿函数实现分析

”test.h“ 中:

其中:

这个完成了偏特化,将 string 可以变成 整型数,方便计算下标 hashi

细节注意

”my_map.h“ 中 :

”my_set.h“ 中 :

确保 map 和 set 都可以使用,

如果类型是 map ,得到的就是 pair<K,V>里面的 K 类型

如果类型是 set ,得到的就是 K 类型

迭代器问题分析

由于在operator++过程中,需要访问HashBucket的成员变量,所以需要友元

且在构造时需要HashBucket类,则需要前置声明

(前置声明)

迭代器注意:

  1. pair<const_Iterator, bool> insert(const pair<K, V>& data)
  2. {
  3. return pair<const_Iterator, bool>(const_Iterator(key.insert(data).first._node,&key), key.insert(data).second);
  4. }

// return pair<const_Iterator, bool>(key.insert(data)) 是错误的

key.insert(data)是 pair<Iterator, bool> 类型,普通迭代器不能转换成 const迭代器

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

闽ICP备14008679号