当前位置:   article > 正文

哈希表及unordered_map、unordered_set的模拟实现_哈希表使用方法unorderedmap

哈希表使用方法unorderedmap

一、哈希表的概念

顺序结构及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须经过关键码的多次比较,顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(log_2 N),搜索的效率取决于搜索过程中元素的比较次数。

如果有一种数据结构,存储的数据与存储的位置呈映射关系,那么不需要进行比较直接通过对应的位置找到映射的数据。

其实这种数据结构就叫哈希表或者散列表,插入的数据通过哈希函数得到对应的位置,然后数据就插入在表中对应的位置上,

例如,我们在表中依次插入1、4、5、6、7、9,这里我们用的哈希函数是hash(key) = key % capacity,假设这个表的容量是10,并且插入的数据都小于容量,那么插入位置的下标和数据是相等的:

 那如果插入一个比容量大的数据呢?比如35呢?

35%10=5,可是下标为5的位置已经有数据了,那怎么办?

这个时候就会发生一个事情叫哈希冲突哈希碰撞(即不同的数据通过相同的函数得到相同的地址)。

二、哈希函数

引起哈希冲突的其中一个原因可能是哈希函数设计的不够合理,我们先了解哈希函数的设计原则:

1、哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址,其值域必须在0~(m-1)之间。

2、哈希函数计算出来的地址能均匀分布在整个空间中。

3、哈希函数应该比较简单。

常用的哈希函数:

1、直接定址法,哈希函数公式就是:hash(key) = A * key + B。

这个方法比较简单,但是它有局限性,它不能应用在海量数据中,太费空间了,适合用在数据比较小并且比较密集的情况。

2、除留余数法,设哈希表中允许的地址数为m,取一个接近或者等于m的质数p作除数,按照哈希函数hash(key) = key % p将关键码转换为哈希地址。

3、平方取中法,假设关键字为1234,它的平方就是1522756,取中间的三位数227作为哈希地址,平方取中法比较适合不知道关键字的分布,而位数又不是很大的情况。

还有一些其他的就不一一举例了...

三、解决哈希冲突

一、闭散列

当发生哈希冲突时,如果哈希表里还有空位置,那么就从映射的这个位置开始找下一个空位置,找到空位置就填上。

找下一个空位置的方法可以使用线性探测,拿上面那组数据为例,如果再次插入35,通过哈希函数得到哈希地址,为5,但是5这个位置已经有人了,那么就继续从5的位置后面继续走,直到找到空位置再填上,如果走到表的末尾还是没有空位置那就再从表头开始找(前提是确定有空位置),但是有一个情况要考虑,那就是删除的时候,首先我们先把35插入进去,经过线性探测最后是在哈希地址为8的位置停下并插入

之后我们再删除5,如果我们还想继续删除35,发现35通过哈希函数得到的哈希地址对应的位置没有数据,会误以为表中没有35。 

解决办法:把表中每个数据都加一个状态信息,分为三种状态,一个是默认的空状态(EMPTY),一个是存在状态(EXIST),一个为删除状态(DELETE)。如果我们删除数据的话只要把状态改为删除即可。

那要扩容的情况呢?

哈希表的载荷因子定义为 α = 填入表中的元素个数 / 哈希表长度,元素个数越多,载荷因子越大,发生哈希冲突的可能性也越大,反之越小,我们最好把荷载因子定义在0.7-0.8之间。

闭散列的实现:

  1. enum State { EMPTY, EXIST, DELETE };
  2. /*template<class T>
  3. class HashFunction
  4. {
  5. public:
  6. size_t operator()(const T& val)
  7. {
  8. return val.first;
  9. }
  10. };*/
  11. template<class K, class V>
  12. class HashTable
  13. {
  14. struct Elem
  15. {
  16. pair<K, V> _val;
  17. State _state;
  18. };
  19. public:
  20. HashTable(size_t capacity = 3)
  21. : _ht(capacity), _size(0), _totalSize(0)
  22. {
  23. for (size_t i = 0; i < capacity; ++i)
  24. _ht[i]._state = EMPTY;
  25. }
  26. // 插入
  27. bool Insert(const pair<K, V>& val)
  28. {
  29. if (Find(val.first) != -1)
  30. return false;
  31. CheckCapacity();
  32. size_t hashi = HashFunc(val.first);
  33. size_t index = hashi;
  34. size_t i = 1;
  35. while (_ht[index]._state == EXIST)
  36. {
  37. index = hashi + i;
  38. index %= _ht.size();
  39. ++i;
  40. }
  41. _ht[index]._val = val;
  42. _ht[index]._state = EXIST;
  43. ++_size;
  44. ++_totalSize;
  45. return true;
  46. }
  47. // 查找
  48. size_t Find(const K& key)
  49. {
  50. size_t hashi = HashFunc(key);
  51. size_t i = 1;
  52. size_t index = hashi;
  53. while (_ht[index]._state != EMPTY)
  54. {
  55. if (_ht[index]._state == EXIST && _ht[index]._val == make_pair(key,key))
  56. return index;
  57. index = hashi + i;
  58. index %= _ht.size();
  59. ++i;
  60. if (index == hashi)
  61. break;
  62. }
  63. return -1;
  64. }
  65. // 删除
  66. bool Erase(const K& key)
  67. {
  68. //size_t hashi = HashFunc(key) % _ht.size();
  69. if (Find(key) != -1)
  70. {
  71. _ht[Find(key)]._state = DELETE;
  72. --_size;
  73. return true;
  74. }
  75. return false;
  76. }
  77. size_t Size()const
  78. {
  79. return _size;
  80. }
  81. bool Empty() const
  82. {
  83. return _size == 0;
  84. }
  85. void Swap(HashTable<K, V>& ht)
  86. {
  87. swap(_size, ht._size);
  88. swap(_totalSize, ht._totalSize);
  89. _ht.swap(ht._ht);
  90. }
  91. private:
  92. size_t HashFunc(const K& key)
  93. {
  94. return key % _ht.capacity();
  95. }
  96. void CheckCapacity()
  97. {
  98. if (_ht.size() == 0 || _size * 10 / _ht.size() >= 7)
  99. {
  100. size_t newsize = _ht.size() == 0 ? 10 : _ht.size() * 2;
  101. HashTable<K, V> newtable;
  102. newtable._ht.resize(newsize);
  103. for(auto& e : _ht)
  104. {
  105. if (e._state == EXIST)
  106. newtable.Insert(e._val);
  107. }
  108. this->Swap(newtable);
  109. }
  110. }
  111. private:
  112. vector<Elem> _ht;
  113. size_t _size;
  114. size_t _totalSize; // 哈希表中的所有元素:有效和已删除, 扩容时候要用到
  115. };

二、开散列

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

开散列的实现:

  1. template<class T>
  2. class HashFunc
  3. {
  4. public:
  5. size_t operator()(const T& val)
  6. {
  7. return val;
  8. }
  9. };
  10. template<>
  11. class HashFunc<string>
  12. {
  13. public:
  14. size_t operator()(const string& s)
  15. {
  16. const char* str = s.c_str();
  17. unsigned int seed = 131; // 31 131 1313 13131 131313
  18. unsigned int hash = 0;
  19. while (*str)
  20. {
  21. hash = hash * seed + (*str++);
  22. }
  23. return hash;
  24. }
  25. };
  26. template<class V>
  27. struct HashBucketNode
  28. {
  29. HashBucketNode(const V& data)
  30. : _pNext(nullptr), _data(data)
  31. {}
  32. HashBucketNode<V>* _pNext;
  33. V _data;
  34. };
  35. template<class V, class HF>
  36. class HashBucket;
  37. template<class T,class Ref,class Ptr,class HF = HashFunc<T>>
  38. struct HashIterator
  39. {
  40. typedef HashBucketNode<T> Node;
  41. typedef HashBucket<T,HF> HT;
  42. typedef HashIterator<T,Ref,Ptr,HF> Self;
  43. typedef HashIterator<T,T&,T*> Iterator;
  44. HashIterator(const Iterator& it)
  45. :_ht(it._ht)
  46. ,_node(it._node)
  47. {}
  48. HashIterator(Node* node,HT* ht)
  49. :_ht(ht)
  50. , _node(node)
  51. {}
  52. Self& operator++()
  53. {
  54. if (_node->_pNext)
  55. _node = _node->_pNext;
  56. else
  57. {
  58. size_t hashi = HF()(_node->_data) % _ht->_table.size();
  59. ++hashi;
  60. while (hashi < _ht->_table.size())
  61. {
  62. if (_ht->_table[hashi])
  63. {
  64. _node = _ht->_table[hashi];
  65. break;
  66. }
  67. ++hashi;
  68. }
  69. if (hashi == _ht->_table.size())
  70. _node = nullptr;
  71. }
  72. return *this;
  73. }
  74. Ref operator*()
  75. {
  76. return _node->_data;
  77. }
  78. bool operator!=(const Self& it)
  79. {
  80. return this->_node != it._node;
  81. }
  82. private:
  83. Node* _node;
  84. const HT* _ht;
  85. };
  86. // 本文所实现的哈希桶中key是唯一的
  87. template<class V, class HF = HashFunc<V>>
  88. class HashBucket
  89. {
  90. template<class T, class Ref, class Ptr, class HF>
  91. friend struct HashIterator;
  92. public:
  93. typedef HashBucketNode<V> Node;
  94. typedef Node* PNode;
  95. typedef HashBucket<V, HF> Self;
  96. typedef HashIterator<V, V&, V*, HF> iterator;
  97. public:
  98. HashBucket(size_t capacity = 0)
  99. : _table(GetNextPrime(capacity))
  100. , _size(0)
  101. {}
  102. ~HashBucket()
  103. {
  104. Clear();
  105. }
  106. iterator begin()
  107. {
  108. size_t hashi = 0;
  109. for (; hashi < _table.size(); ++hashi)
  110. {
  111. if (_table[hashi])
  112. return iterator(_table[hashi],this);
  113. }
  114. return iterator(_table[hashi],this);
  115. }
  116. iterator end()
  117. {
  118. return iterator(nullptr,this);
  119. }
  120. size_t GetNextPrime(size_t prime)
  121. {
  122. // SGI
  123. static const int __stl_num_primes = 28;
  124. static const unsigned long __stl_prime_list[__stl_num_primes] =
  125. {
  126. 53, 97, 193, 389, 769,
  127. 1543, 3079, 6151, 12289, 24593,
  128. 49157, 98317, 196613, 393241, 786433,
  129. 1572869, 3145739, 6291469, 12582917, 25165843,
  130. 50331653, 100663319, 201326611, 402653189, 805306457,
  131. 1610612741, 3221225473, 4294967291
  132. };
  133. size_t i = 0;
  134. for (; i < __stl_num_primes; ++i)
  135. {
  136. if (__stl_prime_list[i] > prime)
  137. return __stl_prime_list[i];
  138. }
  139. return __stl_prime_list[i];
  140. }
  141. // 哈希桶中的元素不能重复
  142. Node* Insert(const V& data)
  143. {
  144. PNode node = Find(data);
  145. if (node)
  146. return node;
  147. else
  148. {
  149. //考虑扩容问题
  150. CheckCapacity();
  151. size_t hashi = HF()(data) % _table.size();
  152. Node* newnode = new Node(data);
  153. newnode->_pNext = _table[hashi];
  154. _table[hashi] = newnode;
  155. ++_size;
  156. return newnode;
  157. }
  158. }
  159. // 删除哈希桶中为data的元素(data不会重复)
  160. bool Erase(const V& data)
  161. {
  162. size_t hashi = HF()(data) % _table.size();
  163. PNode node = _table[hashi];
  164. PNode prev = nullptr;
  165. while (node)
  166. {
  167. if (node->_data == data)
  168. {
  169. if (node == _table[hashi])
  170. {
  171. PNode next = node->_pNext;
  172. delete node;
  173. _table[hashi] = next;
  174. return true;
  175. }
  176. else
  177. {
  178. prev->_pNext = node->_pNext;
  179. delete node;
  180. return true;
  181. }
  182. }
  183. else
  184. {
  185. prev = node;
  186. node = node->_pNext;
  187. }
  188. }
  189. return false;
  190. }
  191. Node* Find(const V& data)
  192. {
  193. if (_table.size() == 0)
  194. return nullptr;
  195. size_t hashi = HF()(data) % _table.size();
  196. PNode node = _table[hashi];
  197. while (node != nullptr && node->_data != data)
  198. {
  199. node = node->_pNext;
  200. }
  201. return node;
  202. }
  203. size_t Size()const
  204. {
  205. return _size;
  206. }
  207. bool Empty()const
  208. {
  209. return 0 == _size;
  210. }
  211. void Clear()
  212. {
  213. for (auto& node : _table)
  214. {
  215. PNode cur = node;
  216. while (cur)
  217. {
  218. PNode next = cur->_pNext;
  219. delete cur;
  220. cur = next;
  221. }
  222. }
  223. }
  224. size_t BucketCount()const
  225. {
  226. return _table.capacity();
  227. }
  228. void Swap(Self& ht)
  229. {
  230. _table.swap(ht._table);
  231. swap(_size, ht._size);
  232. }
  233. private:
  234. size_t HashFunc(const V& data)
  235. {
  236. return HF()(data) % _table.capacity();
  237. }
  238. void CheckCapacity()
  239. {
  240. if (_size == _table.size())
  241. {
  242. /*vector<Node*> newtable = new vector<Node*>(_size);*/
  243. Self* newhashbucket = new Self(_size);
  244. for (auto& p : _table)
  245. {
  246. PNode cur = p;
  247. while (cur)
  248. {
  249. PNode next = cur->_pNext;
  250. size_t hashi = HF()(cur->_data) % newhashbucket->_table.size();
  251. cur->_pNext = newhashbucket->_table[hashi];
  252. newhashbucket->_table[hashi] = cur;
  253. cur = next;
  254. }
  255. }
  256. }
  257. }
  258. private:
  259. vector <Node*> _table;
  260. size_t _size; // 哈希表中有效元素的个数
  261. };

在代码的最上方,我们定义了一个仿函数,其目的是存储数据的key不一定是整形,也可能是其他类型,就不方便转换成整型,所以可以通过仿函数转成整形。

四、unordered_set和unordered_map

其实它们的底层就是哈希表,所以模拟实现的话可以对哈希表进行封装就可以了。

一、unordered_set的模拟实现

  1. // 为了实现简单,在哈希桶的迭代器类中需要用到hashBucket本身,
  2. template<class K, class V, class KeyOfValue, class HF>
  3. class HashBucket;
  4. // 注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作
  5. template <class K, class V,class Ref,class Ptr, class KeyOfValue, class HF>
  6. struct HBIterator
  7. {
  8. typedef HashBucket<K, V, KeyOfValue, HF> HashBucket;
  9. typedef HashBucketNode<V>* PNode;
  10. typedef HBIterator<K, V,Ref,Ptr, KeyOfValue, HF> Self;
  11. HBIterator(PNode pNode = nullptr, HashBucket* pHt = nullptr)
  12. :_pNode(pNode)
  13. ,_pHt(pHt)
  14. {}
  15. Self& operator++()
  16. {
  17. // 当前迭代器所指节点后还有节点时直接取其下一个节点
  18. if (_pNode->_pNext)
  19. _pNode = _pNode->_pNext;
  20. else
  21. {
  22. // 找下一个不空的桶,返回该桶中第一个节点
  23. size_t bucketNo = _pHt->HashFunc(KeyOfValue()(_pNode->_data)) + 1;
  24. for (; bucketNo < _pHt->BucketCount(); ++bucketNo)
  25. {
  26. if (_pNode = _pHt->_ht[bucketNo])
  27. break;
  28. }
  29. /*if (bucketNo == _pHt->BucketCount())
  30. _pNode = nullptr;*/
  31. }
  32. return *this;
  33. }
  34. Self operator++(int)
  35. {
  36. Self tmp = *this;
  37. this->operator++();
  38. return tmp;
  39. }
  40. V& operator*()
  41. {
  42. return this->_pNode->_data;
  43. }
  44. V* operator->()
  45. {
  46. return &(this->_pNode->_data);
  47. }
  48. bool operator==(const Self& it) const
  49. {
  50. return this->_pNode == it->_pNode;
  51. }
  52. bool operator!=(const Self& it)const
  53. {
  54. return this->_pNode != it._pNode;
  55. }
  56. PNode _pNode; // 当前迭代器关联的节点
  57. HashBucket* _pHt; // 哈希桶--主要是为了找下一个空桶时候方便
  58. };
  59. // unordered_set中存储的是K类型,HF哈希函数类型
  60. // unordered_set在实现时,只需将hashbucket中的接口重新封装即可
  61. //template<class K, class HF = DefHashF<K>>
  62. template<class K, class HF = HashFunc<K>>
  63. class unordered_set
  64. {
  65. // 通过key获取value的操作
  66. struct KeyOfValue
  67. {
  68. const K& operator()(const K& data)
  69. {
  70. return data;
  71. }
  72. };
  73. typedef OpenHash::HashBucket<K, K, KeyOfValue, HF> HT;
  74. public:
  75. typedef typename HT::const_iterator iterator;
  76. typedef typename HT::const_iterator const_iterator;
  77. public:
  78. unordered_set() : _ht()
  79. {}
  80. iterator begin() { return _ht.begin(); }
  81. iterator end() { return _ht.end(); }
  82. const_iterator begin()const { return _ht.begin(); }
  83. const_iterator end()const { return _ht.end(); }
  84. // capacity
  85. size_t size()const { return _ht.size(); }
  86. bool empty()const { return _ht.empty(); }
  87. ///
  88. // lookup
  89. iterator find(const K& key) { return _ht.Find(key); }
  90. size_t count(const K& key) { return _ht.Count(key); }
  91. /
  92. // modify
  93. pair<iterator, bool> insert(const K& valye)
  94. {
  95. return _ht.Insert(valye);
  96. }
  97. iterator erase(iterator position)
  98. {
  99. return _ht.Erase(position);
  100. }
  101. // bucket
  102. size_t bucket_count() { return _ht.BucketCount(); }
  103. size_t bucket_size(const K& key) { return _ht.Size(key); }
  104. private:
  105. HT _ht;
  106. };

二、unordered_map的模拟实现

  1. // 为了实现简单,在哈希桶的迭代器类中需要用到hashBucket本身,
  2. template<class K, class V, class KeyOfValue, class HF>
  3. class HashBucket;
  4. // 注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作
  5. template <class K, class V, class KeyOfValue, class HF>
  6. struct HBIterator
  7. {
  8. typedef HashBucket<K, V, KeyOfValue, HF> HashBucket;
  9. typedef HashBucketNode<V>* PNode;
  10. typedef HBIterator<K, V, KeyOfValue, HF> Self;
  11. HBIterator(PNode pNode = nullptr, HashBucket* pHt = nullptr)
  12. :_pNode(pNode)
  13. ,_pHt(pHt)
  14. {}
  15. Self& operator++()
  16. {
  17. // 当前迭代器所指节点后还有节点时直接取其下一个节点
  18. if (_pNode->_pNext)
  19. _pNode = _pNode->_pNext;
  20. else
  21. {
  22. // 找下一个不空的桶,返回该桶中第一个节点
  23. size_t bucketNo = _pHt->HashFunc(KeyOfValue()(_pNode->_data)) + 1;
  24. for (; bucketNo < _pHt->BucketCount(); ++bucketNo)
  25. {
  26. if (_pNode = _pHt->_ht[bucketNo])
  27. break;
  28. }
  29. }
  30. return *this;
  31. }
  32. Self operator++(int)
  33. {
  34. Self tmp = this;
  35. this->operator++();
  36. return tmp;
  37. }
  38. V& operator*()
  39. {
  40. return _pNode->_data;
  41. }
  42. V* operator->()
  43. {
  44. return &(_pNode->_data);
  45. }
  46. bool operator==(const Self& it) const
  47. {
  48. return _pNode == it._pNode;
  49. }
  50. bool operator!=(const Self& it) const
  51. {
  52. return _pNode != it._pNode;
  53. }
  54. PNode _pNode; // 当前迭代器关联的节点
  55. HashBucket* _pHt; // 哈希桶--主要是为了找下一个空桶时候方便
  56. };
  57. // unordered_map中存储的是pair<K, V>的键值对,K为key的类型,V为value的类型,HF哈希函数类型
  58. // unordered_map在实现时,只需将hashbucket中的接口重新封装即可
  59. template<class K, class V, class HF = HashFunc<K>>
  60. class unordered_map
  61. {
  62. // 通过key获取value的操作
  63. struct KeyOfValue
  64. {
  65. const K& operator()(const pair<K, V>& data)
  66. {
  67. return data.first;
  68. }
  69. };
  70. typedef OpenHash::HashBucket<K, pair<K, V>, KeyOfValue, HF> HT;
  71. public:
  72. typedef typename HT::iterator iterator;
  73. public:
  74. unordered_map() : _ht()
  75. {}
  76. iterator begin() { return _ht.begin(); }
  77. iterator end() { return _ht.end(); }
  78. // capacity
  79. size_t size()const { return _ht.size(); }
  80. bool empty()const { return _ht.empty(); }
  81. ///
  82. // Acess
  83. V& operator[](const K& key)
  84. {
  85. pair<iterator, bool> ret = insert(make_pair(key, V()));
  86. return ret.first->second;
  87. }
  88. const V& operator[](const K& key)const
  89. {
  90. pair<iterator, bool> ret = insert(make_pair(key, V()));
  91. return ret.fisrt->second;
  92. }
  93. //
  94. // lookup
  95. iterator find(const K& key) { return _ht.Find(key); }
  96. size_t count(const K& key) { return _ht.Count(key); }
  97. /
  98. // modify
  99. pair<iterator, bool> insert(const pair<K, V>& valye)
  100. {
  101. return _ht.Insert(valye);
  102. }
  103. iterator erase(iterator position)
  104. {
  105. return _ht.Erase(position);
  106. }
  107. // bucket
  108. size_t bucket_count() { return _ht.BucketCount(); }
  109. size_t bucket_size(const K& key) { return _ht.BucketSize(key); }
  110. private:
  111. HT _ht;
  112. };

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

闽ICP备14008679号