当前位置:   article > 正文

【C++】-- STL之用哈希桶模拟实现unordered_set和unordered_map_std中的hash桶

std中的hash桶

目录

一、哈希桶节点的修改

二、哈希表

1.构造

2.拷贝构造

3.赋值运算符重载

4.析构

5.迭代器

6.查找

7.插入

8.删除

 三、迭代器

1.operator++( )

2.operator*( )

3.operator->( )

4.operator !=( )

5.operator ==( )

四、封装unordered_set和unordered_map

 1.封装unordered_set

(1)仿函数SetKeyOfT

(2)迭代器

(3)实例

2.封装unordered_map

(1)仿函数MapKeyOfT

(2)迭代器

(3)实例

五、完整代码段


一、哈希桶节点的修改

        用哈希桶封装实现unordered_set和unordered_map,就要考虑到他们传给哈系统的数据元素不同,unordered_set传给哈希桶的是k,unordered_map传给哈希桶的是pair,那么哈希桶面对这两种不同的数据,如何做到统一处理呢?

         面对unordered_set传给哈希桶的是k,unordered_map传给哈希桶的是pair,就把K和V统一封装成T,用T代替pair<K,V>:

  1. template<class T>
  2. struct HashNode
  3. {
  4. HashNode<T>* _next;
  5. T _data;
  6. HashNode(const T& data)
  7. :_data(data)
  8. , _next(nullptr)
  9. {}
  10. };

二、哈希表

类模板需要修改,模板里面必须包含K,因为要用K来计算数据映射的位置。由于哈希桶的节点类型换成了T ,用T来替代V。KeyOfT仿函数确定上传的是unordered_set还是unordered_map。

  1. template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
  2. class HashTable
  3. {
  4. typedef HashNode<T> Node;
  5. //哈希桶迭代器
  6. template<class K,class T,class KeyOfT,class HashFunc>
  7. friend struct __HTIterator;
  8. public:
  9. typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
  10. private:
  11. vector<Node*> _table;
  12. size_t _n = 0;
  13. };

1.构造

使用默认构造函数就可以了,vector自定义类型会调用自己的默认构造函数,size_t作为内置类型编译器不处理:

        HashTable() = default; // 显示指定生成默认构造

2.拷贝构造

_n 直接赋值就可以了。_table的拷贝就需要遍历ht的_table了,并且把ht的_table的每个结点都头插到_table表中:

  1. //拷贝构造
  2. HashTable(const HashTable& ht)
  3. {
  4. _n = ht._n;//存储有效数据的个数一致
  5. _table.resize(ht._table.size());//开同样大小的空间
  6. //遍历ht,将ht的_table的每个结点都拷贝到_table中
  7. for (size_t i = 0; i < ht._table.size(); i++)
  8. {
  9. Node* cur = ht._table[i];
  10. while (cur)
  11. {
  12. Node* copy = new Node(cur->_data);
  13. //头插到新表
  14. copy->_next = _table[i];//copy的下一个桶为_table[i]
  15. _table[i] = copy;//把copy作为当前位置的第一个桶
  16. cur = cur->_next;//cur往下移
  17. }
  18. }
  19. }

3.赋值运算符重载

只需要交换_table和_n即可:

  1. //赋值运算符重载
  2. HashTable& operator=(HashTable ht)
  3. {
  4. _table.swap(ht._table);
  5. swap(_n, ht._n);
  6. return *this;
  7. }

4.析构

只需要将_table 的每个结点删除后置空就可以了:

  1. //析构
  2. ~HashTable()
  3. {
  4. for (size_t i = 0; i < _table.size(); i++)
  5. {
  6. Node* cur = _table[i];
  7. while (cur)
  8. {
  9. Node* next = cur->_next;
  10. delete cur;
  11. cur = next;
  12. }
  13. _table[i] = nullptr;
  14. }
  15. }

5.迭代器

迭代器的参数包含节点位置和哈希表地址,在下一节迭代器中会讲,为什么都要使用指针:

  1. //迭代器开始
  2. iterator begin()
  3. {
  4. size_t i = 0;
  5. while (i < _table.size())
  6. {
  7. if (_table[i])
  8. {
  9. return iterator(_table[i], this);
  10. }
  11. ++i;
  12. }
  13. return end();
  14. }
  15. //迭代器结束
  16. iterator end()
  17. {
  18. return iterator(nullptr, this);
  19. }

6.查找

 这时候就要用到仿函数KeyOfT了,仿函数KeyOfT的对象kot对于unordered_set会取k,对于unordered_map会取作为pair的kv的first作为k和key进行比较:

  1. //查找
  2. iterator Find(const K& key)
  3. {
  4. //哈希表为空
  5. if (_table.size() == 0)
  6. {
  7. return end();
  8. }
  9. KeyOfT kot;
  10. HashFunc hf;
  11. size_t index = hf(key) % _table.size();//计算在哈希表中的位置
  12. //在哈希表当前位置的所有桶中找key
  13. Node* cur = _table[index];
  14. while (cur)
  15. {
  16. if (kot(cur->_data) == key)
  17. {
  18. return iterator(cur,this);
  19. }
  20. else
  21. {
  22. cur = cur->_next;
  23. }
  24. }
  25. return end();
  26. }

7.插入

①需要先判断data在不在哈希桶中,在就直接返回查找到的位置

②如果不在,需要判断哈希桶需不需要增容,如果不需要增容就计算映射位置头插到哈希表中

③需要增容就要取旧表中的节点一一头插到新表中,并交换旧表和新表

  1. //插入
  2. pair<iterator,bool> Insert(const T& data)
  3. {
  4. KeyOfT kot;
  5. auto ret = Find(kot(data));
  6. if (ret != end())
  7. {
  8. return make_pair(ret,false);
  9. }
  10. //仿函数
  11. HashFunc hf;
  12. //负载因子为1时,进行增容
  13. if (_n == _table.size())
  14. {
  15. vector<Node*> newTable;
  16. newTable.resize(GetNextPrime(_table.size()));
  17. //取旧表中的结点,重新计算映射到新表中的位置,挂到新表中
  18. for (size_t i = 0; i < _table.size(); i++)
  19. {
  20. if (_table[i])
  21. {
  22. Node* cur = _table[i];
  23. while (cur)
  24. {
  25. Node* next = cur->_next;//保存下一个结点
  26. size_t index = hf(kot(cur->_data)) % newTable.size();//计算结映射到新表中的位置
  27. //头插
  28. cur->_next = newTable[index];//=nullptr,将cur->_next置空
  29. newTable[index] = cur;//将结点插入到新表
  30. cur = next;//哈希桶往下挪一个
  31. }
  32. _table[i] = nullptr;//当前哈希桶的第一个位置置空
  33. }
  34. }
  35. _table.swap(newTable);
  36. }
  37. //不需要增容时,头插
  38. size_t index = hf(kot(data)) % _table.size();
  39. Node* newNode = new Node(data);
  40. newNode->_next = _table[index];//让新节点newNode的next指向第一个桶
  41. _table[index] = newNode;//让新节点newNode做第一个桶
  42. ++_n;//更新哈希表大小
  43. return make_pair(iterator(newNode, this), true);
  44. }

8.删除

①删除节点之前要先保留该节点的前一个 节点,否则删除改节点后,让前一个节点要指向下一个,但是又找不到前一个节点了。

②当找到key的映射位置后,要判断找到的节点是不是当前位置的第一个桶,如果是,就让当前位置指向下一个节点;如果不是就直接让前一个节点指向后一个节点。

  1. //删除
  2. bool Erase(const K& key)
  3. {
  4. size_t index = hf(key) % _table.size();
  5. Node* prev = nullptr;
  6. Node* cur = _table[index];
  7. while (cur)
  8. {
  9. if (kot(cur->data) == key)//cur这个桶就是key
  10. {
  11. if (_table[index] == cur)//cur是第一个桶
  12. {
  13. _table[index] = cur->_next;
  14. }
  15. else//cur不是第一个桶
  16. {
  17. prev->_next = cur->_next;
  18. }
  19. --_n;//更新表大小
  20. delete cur;//删除节点
  21. return true;
  22. }
  23. prev = cur;
  24. cur = cur->_next;
  25. }
  26. return false;
  27. }

 三、迭代器

        迭代器需要前置声明HashTable,因为HashTable类中使用了__HTIterator迭代器,且__HTIterator中使用了HashTable类的指针,为什么要用指针呢?
        因为C++编译器自上而下编译源文件的时候,对每一个数据的定义,需要知道定义的数据类型的大小。在预先声明语句class HashTable;之后,编译器已经知道HashTable是一个类,但是其中的数据却是未知的,因此HashTable类型的大小也不知道,这样就造成了编译失败;将__HTIterator中的_node更改为HashTable指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节,在64位平台上是8字节),这样可以通过编译。
       

迭代器的参数包含哈希节点和哈希表:

  1. // 前置声明
  2. template<class K, class T, class KeyOfT, class HashFunc>
  3. class HashTable;
  4. // 迭代器
  5. template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
  6. struct __HTIterator
  7. {
  8. typedef HashNode<T> Node;//哈希节点
  9. typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;//实现++、--
  10. typedef HashTable<K, T, KeyOfT, HashFunc> HT;//哈希表
  11. Node* _node;
  12. HT* _pht;
  13. __HTIterator(Node* node, HT* pht)
  14. :_node(node)//哈希节点
  15. , _pht(pht)//哈希表
  16. {}
  17. };

1.operator++( )

如下图,operator++ 分两种情况:

①当前节点不是哈希表当前位置的最后一个节点,如2、53,返回当前节点的next节点

②当前节点是哈希表当前位置的最后一个节点,如852、63,需要返回下一个非空位置的第一个节点

 

  1. Self& operator++()
  2. {
  3. //不是当前位置最后一个节点
  4. if (_node->_next)
  5. {
  6. _node = _node->_next;
  7. }
  8. else//是当前位置最后一个节点
  9. {
  10. KeyOfT kot;
  11. HashFunc hf;
  12. size_t index = hf(kot(_node->_data)) % _pht->_table.size();
  13. //找哈希表中下一个不为空的位置
  14. ++index;
  15. while (index < _pht->_table.size())
  16. {
  17. if (_pht->_table[index])//不为空
  18. {
  19. _node = _pht->_table[index];
  20. return *this;
  21. }
  22. else//为空
  23. {
  24. ++index;
  25. }
  26. }
  27. _node = nullptr;//后面的所有位置都为空,_node置空,返回当前位置即可
  28. }
  29. return *this;
  30. }

2.operator*( )

取_data即可: 

  1. T& operator*()
  2. {
  3. return _node->_data;
  4. }

3.operator->( )

 取_data的地址即可:

  1. T* operator->()
  2. {
  3. return &_node->_data;
  4. }

4.operator !=( )

将s的_node和_node进行比较:

  1. bool operator != (const Self& s) const
  2. {
  3. return _node != s._node;
  4. }

5.operator ==( )

将s的_node和_node进行比较:

  1. bool operator == (const Self& s) const
  2. {
  3. return _node == s._node;
  4. }

四、封装unordered_set和unordered_map

 1.封装unordered_set

unordered_set的成员就是哈希表,不过要传模板参数,SetKeyOfT就是传给哈希表的仿函数

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace delia
  4. {
  5. template<class K>
  6. class unordered_set
  7. {
  8. private:
  9. OpenHash::HashTable<K, K, SetKeyOfT> _ht ;
  10. };
  11. }

(1)仿函数SetKeyOfT

 set直接取k:

  1. //set就取k
  2. struct SetKeyOfT
  3. {
  4. const K& operator()(const K& k)
  5. {
  6. return k;
  7. }
  8. };

(2)迭代器

        对于unordered_set的迭代器,如果不加typename,类模板里面找迭代器的类型找不到,因为OpenHash::HashTable<K, K, SetKeyOfT>::iterator没有实例化,因此编译器并不知道它是类型还是成员函数或成员变量,编译无法通过。
        用typename表明OpenHash::HashTable<K, K, SetKeyOfT>::iterator是一个类型,不是成员函数或成员变量,不需要等到实例化时才能确定。

        unordered_set的迭代器都调用哈希表的迭代器进行操作:

  1. public:
  2. typedef typename OpenHash::HashTable<K, K, SetKeyOfT>::iterator iterator;
  3. iterator begin()
  4. {
  5. return _ht.begin();
  6. }
  7. iterator end()
  8. {
  9. return _ht.end();
  10. }
  11. pair<iterator, bool> Insert(const K& k)
  12. {
  13. return _ht.Insert(k);
  14. }

(3)实例

向unordered_set中插入一些数据,并打印:

  1. void test_unordered_set1()
  2. {
  3. unordered_set<int> us;
  4. us.Insert(100);
  5. us.Insert(5);
  6. us.Insert(6);
  7. us.Insert(32);
  8. us.Insert(8);
  9. us.Insert(14);
  10. us.Insert(65);
  11. us.Insert(27);
  12. us.Insert(39);
  13. unordered_set<int>::iterator it = us.begin();
  14. while (it != us.end())
  15. {
  16. cout << *it << " ";
  17. ++it;
  18. }
  19. cout << endl;
  20. }

2.封装unordered_map

unordered_map的成员就是哈希表,不过要传模板参数,MapKeyOfT就是传给哈希表的仿函数 :

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace delia
  4. {
  5. template<class K,class V>
  6. class unordered_map
  7. {
  8. private:
  9. OpenHash::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
  10. };

(1)仿函数MapKeyOfT

map就取kv的first: 

  1. //map就取kv的first
  2. struct MapKeyOfT
  3. {
  4. const K& operator()(const pair<K,V>& kv)
  5. {
  6. return kv.first;
  7. }
  8. };

(2)迭代器

  同unordered_set的迭代器定义一样,前面要加上typename,unordered_map的迭代器都调用哈希表的迭代器进行操作:

  1. public:
  2. typedef typename OpenHash::HashTable<K, pair<K,V>, MapKeyOfT>::iterator iterator;
  3. iterator begin()
  4. {
  5. return _ht.begin();
  6. }
  7. iterator end()
  8. {
  9. return _ht.end();
  10. }
  11. pair<iterator, bool> Insert(const pair<K,V>& kv)
  12. {
  13. return _ht.Insert(kv);
  14. }
  15. V& operator[](const K& key)
  16. {
  17. pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
  18. return ret.first->iterator;
  19. }

(3)实例

  1. void test_unordered_map1()
  2. {
  3. unordered_map<string,string> um;
  4. um.Insert(make_pair("spring", "春天"));
  5. um.Insert(make_pair("summer", "夏天"));
  6. um.Insert(make_pair("autumn", "秋天"));
  7. um.Insert(make_pair("winter", "冬天"));
  8. unordered_map<string,string>::iterator it = um.begin();
  9. while (it != um.end())
  10. {
  11. cout << it->first << ":" << it->second << endl;
  12. ++it;
  13. }
  14. }

五、完整代码段

HashTable.h

  1. #pragma once
  2. #include<vector>
  3. #include<iostream>
  4. using namespace std;
  5. namespace OpenHash
  6. {
  7. //哈希仿函数
  8. template<class K>
  9. struct Hash
  10. {
  11. size_t operator()(const K& key)
  12. {
  13. return key;
  14. }
  15. };
  16. //string仿函数
  17. template<>
  18. struct Hash<string>//模板特化
  19. {
  20. size_t operator()(const string& s)
  21. {
  22. size_t value = 0;
  23. for (auto e : s)
  24. {
  25. value += e;
  26. value *= 131;
  27. }
  28. return value;
  29. }
  30. };
  31. template<class T>
  32. struct HashNode
  33. {
  34. HashNode<T>* _next;
  35. T _data;
  36. HashNode(const T& data)
  37. :_data(data)
  38. , _next(nullptr)
  39. {}
  40. };
  41. //前置声明HashTable,因为HashTable类中使用了__HTIterator,且__HTIterator中使用了HashTable类的指针,为什么要用指针
  42. //C++编译器自上而下编译源文件的时候,对每一个数据的定义,总是需要知道定义的数据类型的大小。在预先声明语句class HashTable;之后,编译器已经知道HashTable是一个类,但是其中的数据却是未知的,因此HashTable类型的大小也不知道,这样就造成了编译失败;将__HTIterator中的_node更改为HashTable指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节,在64位平台上是8字节),这样可以通过编译
  43. // 前置声明
  44. template<class K, class T, class KeyOfT, class HashFunc>
  45. class HashTable;
  46. // 迭代器
  47. template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
  48. struct __HTIterator
  49. {
  50. typedef HashNode<T> Node;//哈希节点
  51. typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;//实现++、--
  52. typedef HashTable<K, T, KeyOfT, HashFunc> HT;//哈希表
  53. Node* _node;
  54. HT* _pht;
  55. __HTIterator(Node* node, HT* pht)
  56. :_node(node)//哈希节点
  57. , _pht(pht)//哈希表
  58. {}
  59. Self& operator++()
  60. {
  61. //不是当前位置最后一个节点
  62. if (_node->_next)
  63. {
  64. _node = _node->_next;
  65. }
  66. else//是当前位置最后一个节点
  67. {
  68. KeyOfT kot;
  69. HashFunc hf;
  70. size_t index = hf(kot(_node->_data)) % _pht->_table.size();
  71. //找哈希表中下一个不为空的位置
  72. ++index;
  73. while (index < _pht->_table.size())
  74. {
  75. if (_pht->_table[index])//不为空
  76. {
  77. _node = _pht->_table[index];
  78. return *this;
  79. }
  80. else//为空
  81. {
  82. ++index;
  83. }
  84. }
  85. _node = nullptr;//后面的所有位置都为空,_node置空,返回当前位置即可
  86. }
  87. return *this;
  88. }
  89. T& operator*()
  90. {
  91. return _node->_data;
  92. }
  93. T* operator->()
  94. {
  95. return &_node->_data;
  96. }
  97. bool operator != (const Self& s) const
  98. {
  99. return _node != s._node;
  100. }
  101. bool operator == (const Self& s) const
  102. {
  103. return _node == s._node;
  104. }
  105. };
  106. template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
  107. class HashTable
  108. {
  109. typedef HashNode<T> Node;
  110. template<class K,class T,class KeyOfT,class HashFunc>
  111. friend struct __HTIterator;
  112. public:
  113. typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
  114. //构造函数,default指定生成默认构造函数
  115. HashTable() = default;
  116. //拷贝构造
  117. HashTable(const HashTable& ht)
  118. {
  119. _n = ht._n;//存储有效数据的个数一致
  120. _table.resize(ht._table.size());//开同样大小的空间
  121. //遍历ht,将ht的每个结点都拷贝到_table中
  122. for (size_t i = 0; i < ht._table.size(); i++)
  123. {
  124. Node* cur = ht._table[i];
  125. while (cur)
  126. {
  127. Node* copy = new Node(cur->_data);
  128. //头插到新表
  129. copy->_next = _table[i];//copy的下一个桶为_table[i]
  130. _table[i] = copy;//把copy作为当前位置的第一个桶
  131. cur = cur->_next;//cur往下移
  132. }
  133. }
  134. }
  135. //赋值运算符重载
  136. HashTable& operator=(HashTable ht)
  137. {
  138. _table.swap(ht._table);
  139. swap(_n, ht._n);
  140. return *this;
  141. }
  142. //析构
  143. ~HashTable()
  144. {
  145. for (size_t i = 0; i < _table.size(); i++)
  146. {
  147. Node* cur = _table[i];
  148. while (cur)
  149. {
  150. Node* next = cur->_next;
  151. delete cur;
  152. cur = next;
  153. }
  154. _table[i] = nullptr;
  155. }
  156. }
  157. iterator begin()
  158. {
  159. size_t i = 0;
  160. while (i < _table.size())
  161. {
  162. if (_table[i])
  163. {
  164. return iterator(_table[i], this);
  165. }
  166. ++i;
  167. }
  168. return end();
  169. }
  170. iterator end()
  171. {
  172. return iterator(nullptr, this);
  173. }
  174. size_t GetNextPrime(size_t prime)
  175. {
  176. const int PRIMECOUNT = 28;
  177. static const size_t primeList[PRIMECOUNT] =
  178. {
  179. 53ul,97ul,193ul,389ul,769ul,
  180. 1543ul,3079ul,6151ul,12289ul,24593ul,
  181. 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
  182. 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
  183. 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
  184. 1610612741ul, 3221225473ul, 4294967291ul
  185. };
  186. size_t i = 0;
  187. for (; i < PRIMECOUNT; i++)
  188. {
  189. if (primeList[i] > prime)
  190. {
  191. return primeList[i];
  192. }
  193. }
  194. return primeList[i];
  195. }
  196. //插入
  197. pair<iterator,bool> Insert(const T& data)
  198. {
  199. KeyOfT kot;
  200. auto ret = Find(kot(data));
  201. if (ret != end())
  202. {
  203. return make_pair(ret,false);
  204. }
  205. //仿函数
  206. HashFunc hf;
  207. //负载因子为1时,进行增容
  208. if (_n == _table.size())
  209. {
  210. vector<Node*> newTable;
  211. newTable.resize(GetNextPrime(_table.size()));
  212. //取旧表中的结点,重新计算映射到新表中的位置,挂到新表中
  213. for (size_t i = 0; i < _table.size(); i++)
  214. {
  215. if (_table[i])
  216. {
  217. Node* cur = _table[i];
  218. while (cur)
  219. {
  220. Node* next = cur->_next;//保存下一个结点
  221. size_t index = hf(kot(cur->_data)) % newTable.size();//计算结映射到新表中的位置
  222. //头插
  223. cur->_next = newTable[index];//=nullptr,将cur->_next置空
  224. newTable[index] = cur;//将结点插入到新表
  225. cur = next;//哈希桶往下挪一个
  226. }
  227. _table[i] = nullptr;//当前哈希桶的第一个位置置空
  228. }
  229. }
  230. _table.swap(newTable);
  231. }
  232. //不需要增容时,头插
  233. size_t index = hf(kot(data)) % _table.size();
  234. Node* newNode = new Node(data);
  235. newNode->_next = _table[index];//让新节点newNode的next指向第一个桶
  236. _table[index] = newNode;//让新节点newNode做第一个桶
  237. ++_n;//更新哈希表大小
  238. return make_pair(iterator(newNode, this), true);
  239. }
  240. //查找
  241. iterator Find(const K& key)
  242. {
  243. //哈希表为空
  244. if (_table.size() == 0)
  245. {
  246. return end();
  247. }
  248. KeyOfT kot;
  249. HashFunc hf;
  250. size_t index = hf(key) % _table.size();//计算在哈希表中的位置
  251. //在哈希表当前位置的所有桶中找key
  252. Node* cur = _table[index];
  253. while (cur)
  254. {
  255. if (kot(cur->_data) == key)
  256. {
  257. return iterator(cur,this);
  258. }
  259. else
  260. {
  261. cur = cur->_next;
  262. }
  263. }
  264. return end();
  265. }
  266. //删除
  267. bool Erase(const K& key)
  268. {
  269. size_t index = hf(key) % _table.size();
  270. Node* prev = nullptr;
  271. Node* cur = _table[index];
  272. while (cur)
  273. {
  274. if (kot(cur->data) == key)//cur这个桶就是key
  275. {
  276. if (_table[index] == cur)//cur是第一个桶
  277. {
  278. _table[index] = cur->_next;
  279. }
  280. else//cur不是第一个桶
  281. {
  282. prev->_next = cur->_next;
  283. }
  284. --_n;//更新表大小
  285. delete cur;//删除节点
  286. return true;
  287. }
  288. prev = cur;
  289. cur = cur->_next;
  290. }
  291. return false;
  292. }
  293. private:
  294. vector<Node*> _table;
  295. size_t _n = 0;
  296. };
  297. }

 UnorderedSet.h

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace delia
  4. {
  5. template<class K>
  6. class unordered_set
  7. {
  8. //set就取k
  9. struct SetKeyOfT
  10. {
  11. const K& operator()(const K& k)
  12. {
  13. return k;
  14. }
  15. };
  16. public:
  17. //如果不加typename,类模板里面找迭代器的类型找不到,因为OpenHash::HashTable<K, K, SetKeyOfT>::iterator没有实例化,因此编译器并不知道它是类型还是成员函数或成员变量,编译无法通过
  18. //用typename表明OpenHash::HashTable<K, K, SetKeyOfT>::iterator是一个类型,不是成员函数或成员变量,不需要等到实例化时才能确定
  19. typedef typename OpenHash::HashTable<K, K, SetKeyOfT>::iterator iterator;
  20. iterator begin()
  21. {
  22. return _ht.begin();
  23. }
  24. iterator end()
  25. {
  26. return _ht.end();
  27. }
  28. pair<iterator, bool> Insert(const K& k)
  29. {
  30. return _ht.Insert(k);
  31. }
  32. private:
  33. OpenHash::HashTable<K, K, SetKeyOfT> _ht ;
  34. };
  35. void test_unordered_set1()
  36. {
  37. unordered_set<int> us;
  38. us.Insert(100);
  39. us.Insert(5);
  40. us.Insert(6);
  41. us.Insert(32);
  42. us.Insert(8);
  43. us.Insert(14);
  44. us.Insert(65);
  45. us.Insert(27);
  46. us.Insert(39);
  47. unordered_set<int>::iterator it = us.begin();
  48. while (it != us.end())
  49. {
  50. cout << *it << " ";
  51. ++it;
  52. }
  53. cout << endl;
  54. }
  55. }

UnorderedMap.h

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace delia
  4. {
  5. template<class K,class V>
  6. class unordered_map
  7. {
  8. //map就取kv的first
  9. struct MapKeyOfT
  10. {
  11. const K& operator()(const pair<K,V>& kv)
  12. {
  13. return kv.first;
  14. }
  15. };
  16. public:
  17. typedef typename OpenHash::HashTable<K, pair<K,V>, MapKeyOfT>::iterator iterator;
  18. iterator begin()
  19. {
  20. return _ht.begin();
  21. }
  22. iterator end()
  23. {
  24. return _ht.end();
  25. }
  26. pair<iterator, bool> Insert(const pair<K,V>& kv)
  27. {
  28. return _ht.Insert(kv);
  29. }
  30. V& operator[](const K& key)
  31. {
  32. pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
  33. return ret.first->iterator;
  34. }
  35. private:
  36. OpenHash::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
  37. };
  38. void test_unordered_map1()
  39. {
  40. unordered_map<string,string> um;
  41. um.Insert(make_pair("spring", "春天"));
  42. um.Insert(make_pair("summer", "夏天"));
  43. um.Insert(make_pair("autumn", "秋天"));
  44. um.Insert(make_pair("winter", "冬天"));
  45. unordered_map<string,string>::iterator it = um.begin();
  46. while (it != um.end())
  47. {
  48. cout << it->first << ":" << it->second << endl;
  49. ++it;
  50. }
  51. }
  52. }

Test.cpp

  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include "HashTable.h"
  3. #include "UnorderedSet.h"
  4. #include "UnorderedMap.h"
  5. int main()
  6. {
  7. delia::test_unordered_set1();
  8. delia::test_unordered_map1();
  9. return 0;
  10. }

运行结果:

unordered_set:

由于哈希表的初始大小为primeList中的第一个元素53,因此插入时:

100%53=47,

5%53=5,

6%53=6,

8%53=8,

65%53=12,

14%53=14,

27%53=27,

32%53=32,

39%53=39

且都没有发生冲突,因此排列顺序为5,6,8,65,14,27,32,39,100。

 

unordered_map:

字符串的Hash算法让每个字符*131再求和,再对53取模:

ASCII对照表如下:

97a
98b
99c
100d
101e
102f
103g
104h
105i
106j
107k
108l
109m
110n
111o
112p
113q
114r
115s
116t
117u
118v
119w
120x
121y
122z

spring:(((((((115*131)+112)*131)+114)*131+105)*131+110)*131+103)*131=359074819,中间发生了溢出,所以得到结果359074819,再用359074819对53取模,sunmmer,autumn和winter也都这么计算


 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号