当前位置:   article > 正文

通俗易懂->哈希表详解

哈希表

目录

一、什么是哈希表?

1.1哈希表长什么样?

1.2为什么会有哈希表?

1.3哈希表的特点

1.3.1 取余法、线性探测

1.3.2 映射

1.3.3负载因子

1.4哈希桶

1.5闲散列与开散列

1.6总结

二、设计hash表

1、哈希表的设计

  1)插入

  2)查找

 3)删除

4)字符串哈希算法

2、封装map和set

1、完成对hash表的基础功能

2、完成封装

3、对应的迭代器

4、【】方括号重载

三、设计原码

1、HashTable

2、unordered_map

3、unordered_set

4、test


一、什么是哈希表?

什么是哈希表?

哈希表,顾名思义,就是一个表。

可是为什么叫哈希表?

因为这是从老美哪里音译过来的

叫做->Hash Table

翻译过来就是->哈希表

既然是表,那么

第一,这个哈希表长什么样子?

第二,为什么会有这个哈希表?

第三,这个哈希表用来做什么?

第三,这个哈希表的特点是什么?

第四,什么是取余法?

第五,什么是映射?

第六,什么是线性探测?

第七,什么是哈希桶?

一些常见的概念,是什么?要怎么理解?

下面一一我来解析。

1.1哈希表长什么样?

一般哈希表有两种形式(先别问为什么,先看,后面解释)

1.2为什么会有哈希表?

假设你有一个数组或者链表,

传统的数组访问某一个数据,或者链表访问某一个数据,必须遍历,也只有遍历。

假如你的数组长度为100万,你现在要取某个值,而你知道这个值在就在数组的中间。

怎么办?

此时,无论从前往后,还是从后往前遍历,都绕不过50万个值。

蛋不蛋疼?蛋疼。

难受不难受?难受。

如果数据规模更大,例如100亿,那更难受。

所以,有困难,有麻烦,就会引发思考:我能不能不用遍历,咔的一下,马上就找到这个值?

而传统的数据结构显然无法突破这个难题。

既然旧的不行,干脆,那就搞一个新的。

于是,天空一声巨响,哈希表闪亮登场!

所以,哈希表就是为了解决查找必须遍历的问题而生。

1.3哈希表的特点

如何做到不遍历直接访问到数据?

很简单,非常简单,简单到不能再简单。

举个例子,你有5个值:1, 2, 3, 4 ,10001

我在创建数组的时候,我直接申请10001个空间!

然后所有的数据直接存储在对应的下标位置。

(啊???)

现在你给我一个值,例如说10001。

要我去表里找,此时我不去遍历。

我直接就到数组下标为10001的位置取数据。

因为数组是支持随机访问的。

我直接初始位置+10001,直接就拿到数据了。

有毛病没有?没有。

有问题没有?没有。

你会说,是不是太浪费空间了?

是的,非常浪费。

但是,你就说我遍历了没有?没有。

你就说找到了没有?找到了。

你就说快不快?非常快,O(1)。

所以,解决我们原来要遍历的困扰没有?解决了。

但是,你会发现不对劲,空间太浪费了啊。

假设我有只有2个数据,一个是1,另一个是1000000000000000。

(嗯......?!)

难道我要建立那么大的空间,就为了存这俩货?

不行,很不行,非常不行。

那么,怎么办?

于是,取余法就来了。

1.3.1 取余法、线性探测

现在,我们回到文章开始的地方,哈希表的模样,其中一种是这样的:

为什么是这样的?

下面我们来解释:

假如,我们有以上数据:2,18,22,89,1000000000001

按照原来的办法,我们需要开辟10000000000001个空间,才能构成哈希表

即数据存储在对应位置

但是,这种方式不行,太浪费空间。

怎么办?取余法。

什么是取余法?

很简单,就是取余数。

例如,我们现在有5个数据,那么我们就开10个空间

然后,让这每一个数据%10,得到结果

再将这个结果放到对应的位置。

什么意思?

很简单,现在有6个数据:2,18,22,23,89,1000000000001

2%10 = 2,所以2存放在下标为2的位置

18%10 = 8,所以2存放在下标为8的位置

22%10 = 2,所以存放在下标为2的位置

但是,现在下标为2的位置已经有了一个值

而这个,就是所谓的哈希冲突!非常简单,非常好理解对不对?

是的,就是这么简单。不要被一些看似很牛b的概念给困住了。

所有的概念都只不过是为了更好的概括和综合某个现象,方便于你理解,仅此而已。

学习新事物,也是如此。

好的,那么,这个位置冲突了,怎么办?

那就放到后面没有冲突的位置。

下标为3的位置没有冲突,所以,放到3

23%10 = 3,所以23存放在下标为3的位置

但是,同样的,下标为3的位置已经有数据了。

即所谓哈希冲突了。

怎么办?

很简单,同样的道理,往后挪

挪到没有冲突的位置。

所以,往后下标4的位置没有数据,所以23存在下标为4的位置

89%10 = 9,所以89存放在下标为9的位置

1000000000001%10 = 1,所以1000000000001存放在下标为1的位置

于是,这6个数据,即使有数据为1000000000001那么大的值,

我们也仅仅用了10个空间就存储下来了。

那么,如何取出数据呢?

同样的,很简单,例如取18,将18%10=8,然后到下标为8的位置取即可。

但是,22呢?22%10=2,但是22却不在下标为2的位置。

怎么办呢?

往后找。

23呢?也不在下标为3的位置。

怎么办呢?

往后找。

如果当前位置找不到值,就往后挨个查找,直到找到

往后找,就像是探测,而且是一个一个探测,是线性的查找。

这就是所谓线性探测

所以,我们把这个表,就叫做线性探测表

非常简单。

1.3.2 映射

同时,你会发现:

22并不是存在下标为22的位置,而是存在下标为2的位置

1000000001也不是存在下标为1000000001的位置,而是存在下标为1的位置

这就是所谓映射

即,值与值之间的关联,一种关系。

22和下标为2的位置的映射关系。

1000000001和下标为1的位置的映射关系。

还可以这么理解:

你名字叫做张三,你的发小叫做李四。

别人叫张三,叫的是你,而不是你发小。

这就是映射,张三映射你,李四映射你发小。

这就是一对一映射

同时,还会有这一种情况:

你遇到了一个人,她名字也叫做张三!

现在,有人叫张三的名字,但是张三有两个人。

所以,张三映射对应两个人。

甚至,张三有很多很多,例如说张伟这个名字。

这就是一对多的映射

1.3.3负载因子

取余法,解决数据存放空间太大的问题

好了,现在我们已经解决了空间太大的问题。

但是,问题又来了。

什么问题?

例如,假设我们的数据老是冲突,怎么办?

这个时候的访问,就会偏离我们初始的目的,即不遍历

因为访问一个数据,老是要往后遍历,很麻烦

随着数据的增多,冲突的概率增加,查找的成本越来越高。

也就是说,问题源于数据太多,而空间不够

怎么办?

很简单,扩容。

那么,我们应该什么时候扩容呢?

很简单,用负载因子判断。

好,什么是负载因子?

负载因子就是数据个数所占整个空间的比率。

例如

10个空间,有2个数据

负载因子就是0.2

10个空间,有7个数据

负载因子就是0.7

所以,每插入一个位置,我们就让负载因子+1

而一般来说,负载因子达到0.7就要进行扩容。

1.4哈希桶

回到文章开始,我们说哈希表一般有两种形式,

一个叫做线性探测表,前面已经解释清楚。

另一个,叫做哈希桶。

长这个样子:

我们已经知道哈希桶长什么样子,下面我们来解释:

为什么要有哈希桶?

假如我有一组数据。

这一组数据是:2,22,222,2222,22222,222222,2222222,22222222........

好的,数据我给你了。

你存吧。

你想要怎么存?

如果按照线性探测表的方式进行存储:

好家伙,你一看数据,你发现

取余数,结果全是2

存一个冲突一个

存两个冲突两个

存三个冲突三个

.....

从头冲突到尾,没完没了。

怎么办?

很明显,线性探测形式的哈希表有着致命的弱点

即无法对余数相同的数据进行处理

冲突了,只有往后放

我的位置冲突了,我就去放别人的位置,让别人冲突

(没错,就是这么强大,你打我啊)

如此一来,当数据越来越多时,哈希冲突的概率将会越来越大

哈希冲突多了,就会导致查找的成本越来越高

哈希表的优势也会越来越微弱

怎么办?

于是,哈希桶来了。

我这么存:

我现在无论是存12,22,32,42,52还是222222222222

还会和其他位置冲突吗?

不会。

哈希桶将冲突的值,放到同一个位置下,用单链表管理起来

哈希桶的结构,极大的优化了线性探测表无法处理哈希冲突的缺点

同时,单链表的访问,其时间复杂度也控制在O(n)的量级。

非常棒。

哈希桶也存在负载因子的问题,

和线性探测负载因子是一样的逻辑

同样,也是大于0.7就进行扩容

扩容是对数组的扩容

而扩容之后,单链表的长度也会变短

为什么?

例如空间从10变为100

原来2,12,22,32,42,52,62,72,82,92等值都只能挂在下标2的位置

但是当空间变为100

瞬间,所有的值都有了自己对应的下标位置

原本长度为10的哈希桶,直接变为1

优化效果相当棒

这,就是哈希桶

1.5闲散列与开散列

1、闲散列:开放定址法(线性探测/二次探测)

二次检测:当数据比较集中的时候,查找会比较慢,

为了更快的查找,下一个位置查找偏移量不为1,可以为2次方

思路:我的位置被别人占了,我就去占别人得位置

冲突越多,效率越低

因此,有人提出了开散列

2、开散列:哈希桶/拉链法

1.6总结

综上,我们来总结一下:

1、值很分散,因此哈希表也叫做散列表

2、有些值不好映射,比如string,结构体等

3、开空间问题,即哈希冲突

4、哈希表是用哈希思想实现的一种数据结构

hash更多的来说,是一种思想

例如编码表也是一种哈希编码的运用

例如经典的ASCII码表

同时,有时候你会发现你打开某些文件,会出现乱码

为什么?

因为码表对错了,原本这个文件要拿表A来映射

结果拿成表B来映射了

所以结果就是乱码

乱码的本质是编码表对乱了

以上就是关于哈希表的基础概念和知识。

下面博主要带大家设计出一个简易版哈希表

即unordered_set和unordered_map

使用c++实现,总体还是比较难的

涉及模板、多层嵌套封装、泛型编程、内外部类、友元等

需要有一定的c++基础。

其实简单理解就够了,不必跟着我写出一个。

二、设计hash表

1、哈希表的设计

  1)插入

如果位置不被占,插入;如果位置被占,遇到空才插入      

插入逻辑:

先是数据%size,为什么是size而不是capacity呢?

因为capacity后面的的值是没法访问的,end位置是size前一个位置

然后找到空/被删除的位置,插入,n++

n是记录哈希表个数的值,为什么不用size呢?

因为hash表是离散的表

如果hash表数值很多,就有很大的概论发生冲突

怎么办?

设置一个负载因子,用来记录hash表内的数据个数的占比

一般是0.7~0.8

如果hash表满了呢?在找空/被删除位置时,就会出现死循环的问题

满了就扩容

扩容之后,原有的值不能拷贝,需要重新映射,新的hash表的size是原有空间的2倍

处理数据冗余:插入前利用find

  2)查找

遇到空才停止,但是如果中间位置有空位置,就查不到后面的位置,如何解决?

设置一个位置状态:EMPTY、DELETE,EXIST

 3)删除

删除的值不必抹除,而是将状态设置为DELETE。

如果抹除,设置为什么值都不合适

因此,删除是一个伪删除,查找值时依旧能找到

在Find函数需要特殊判断,即存在才查找

如果数据不能取%怎么办?

例如string数据类型

用仿函数进行解决

对于整型、浮点型、指针都进行size_t强转为整型,就可以取%

但是string不可以转化为整型

怎么办?

单独写一个为string转换的仿函数

这个仿函数返回string【0】

4)字符串哈希算法

把字符串转型为整型

将每一个字符的ASCII值*某个数值,累计加

最后每一个字符串的结果一般都不会重复,但是依旧会重复

string很常见,那可以对仿函数使用特化

什么是特化?就是对某些类进行特殊化处理

就是不适用模板推广,而是直接使用

2、封装map和set

1、完成对hash表的基础功能

一个哈希表要有的功能:

插入、删除、遍历(迭代器)

2、完成封装

插入、删除、查找的接口封装

3、对应的迭代器

        1)首先是一个迭代器对象,完成对象的简单框架

        哈希表的迭代器,需要哈希表本身,以及哈希表对应位置的节点

        其内部的哈希表,需要外部调用对象哈希表来初始化

        2)++重载

                1、先算出当前所在位置

                2、当前位置的下一个位置不为空,那就是有直接返回

                3、当前位置为空,要继续找后面的位置

4、【】方括号重载

返回值是一个pair,第一个参数是迭代器,第二个参数是bool,判断是否添加成功

如果已经存在,返回已存在节点的迭代器,以及false

如果节点不存在,返回新插入节点的迭代器,以及true

需要修改insert的返回值

以及find的返回值

这样就可以直接对find和insert进行复用

方括号重载返回值为value

三、设计原码

1、HashTable

  1. #pragma once
  2. #include<iostream>
  3. #include<vector>
  4. using namespace std;
  5. //设计一个hash表
  6. //哈希表是一个vector数组
  7. //节点存储的是一个pair节点
  8. //使用模板编程
  9. //如果插入的是string,就不能取%
  10. //怎么办?字符串哈希算法
  11. //使用仿函数
  12. enum State
  13. {
  14. EXIST,
  15. DELETE,
  16. EMPTY
  17. };
  18. template<class K>
  19. struct hashFunc
  20. {
  21. size_t operator()(const K& key)
  22. {
  23. return (size_t)key;
  24. }
  25. };
  26. template<>
  27. struct hashFunc<string>
  28. {
  29. size_t operator()(const string& key)
  30. {
  31. size_t ret = 0;
  32. for (auto ch : key)
  33. {
  34. ret *= 131;
  35. ret += ch;
  36. }
  37. return ret;
  38. }
  39. };
  40. namespace hash
  41. {
  42. //哈希表节点
  43. template<class K, class V>
  44. struct HashData
  45. {
  46. pair<K, V> _kv;
  47. State _state = EMPTY;
  48. };
  49. //定义哈希表
  50. template<class K, class V, class Hash = hashFunc<K>>
  51. class HashTable
  52. {
  53. public:
  54. typedef HashData<K, V> Node;
  55. HashTable()
  56. {
  57. _tables.resize(10);
  58. }
  59. //插入
  60. bool Insert(const pair<K, V>& kv)
  61. {
  62. if (Find(kv.first))
  63. {
  64. return false;
  65. }
  66. //检查扩容
  67. if (_n / _tables.size() >= 0.7)
  68. {
  69. size_t newSize = _tables.size() * 2;
  70. HashTable<K,V,Hash> newHashTable;
  71. newHashTable._tables.resize(newSize);
  72. //重新插入新空间
  73. for (size_t i = 0; i < _tables.size(); ++i)
  74. {
  75. newHashTable.Insert(_tables[i]._kv);
  76. }
  77. }
  78. //1、找到插入的位置,取%
  79. Hash hs;
  80. size_t hashi = hs(kv.first) % _tables.size();
  81. while (_tables[hashi]._state != EMPTY)
  82. {
  83. ++hashi;
  84. }
  85. //3、插入,更新负载因子
  86. _tables[hashi]._kv = kv;
  87. _tables[hashi]._state = EXIST;
  88. _n++;
  89. return true;
  90. }
  91. //查找
  92. Node* Find(const K& key)
  93. {
  94. Hash hs;
  95. size_t hashi = hs(key) % _tables.size();
  96. while (_tables[hashi]._state != EMPTY)
  97. {
  98. if (_tables[hashi]._state == EXIST &&
  99. _tables[hashi]._kv.first == key)
  100. {
  101. return &_tables[hashi];
  102. }
  103. ++hashi;
  104. hashi %= _tables.size();
  105. }
  106. return nullptr;
  107. }
  108. size_t size()
  109. {
  110. return _n;
  111. }
  112. //删除
  113. bool Erase(const K& key)
  114. {
  115. Node* cur = Find(key);
  116. if (cur)
  117. {
  118. cur->_state = DELETE;
  119. --_n;
  120. return true;
  121. }
  122. return false;
  123. }
  124. private:
  125. vector<HashData<K, V>> _tables;
  126. size_t _n = 0;
  127. };
  128. void HashTest()
  129. {
  130. HashTable<int, int> ht;
  131. ht.Insert({ 1,1 });
  132. ht.Insert({ 2,2 });
  133. ht.Insert({ 3,3 });
  134. ht.Insert({ 4,4 });
  135. ht.Insert({ 3,3 });
  136. ht.Insert({ 3,3 });
  137. cout << ht.Find(1) << endl;
  138. cout << ht.Find(2) << endl;
  139. cout << ht.Find(3) << endl;
  140. cout << ht.Find(4) << endl;
  141. cout << ht.size() << endl;
  142. ht.Erase(1);
  143. ht.Erase(2);
  144. cout << ht.size() << endl;
  145. }
  146. void HashTest1()
  147. {
  148. HashTable<string, int> ht;
  149. ht.Insert({ "abcd",1});
  150. ht.Insert({ "edasdfas",2});
  151. ht.Insert({ "kahkahdk",3});
  152. ht.Insert({ "ohjahsflhasf",4});
  153. cout << ht.size() << endl;
  154. size_t ret = hashFunc<string>()("dasldfhalf");
  155. size_t ret1 = hashFunc<string>()("sad");
  156. cout << ret << endl;
  157. cout << ret1 << endl;
  158. }
  159. }
  160. //哈希桶实现
  161. namespace hash_bucket
  162. {
  163. //哈希节点,是一个链表
  164. template<class T>
  165. struct HashNode
  166. {
  167. T _data;
  168. HashNode* _next;
  169. HashNode(const T& data)
  170. :_data(data)
  171. , _next(nullptr)
  172. {
  173. }
  174. };
  175. 前置声明
  176. //template<class K, class T, class Hash, class KeyOfT>
  177. //class HashTable;
  178. 实现迭代器
  179. //template<class K, class T, class Hash, class KeyOfT>
  180. //struct __HSIterator//这个是给hash表底层使用的迭代器对象
  181. //{
  182. // typedef HashNode<T> Node;
  183. // typedef HashTable<K, T, Hash, KeyOfT> Self;
  184. // HashTable<K, T, Hash, KeyOfT>* _pht;
  185. // Node* _node;
  186. // __HSIterator(const Node* node, const HashTable<K, T, Hash, KeyOfT>* pht)
  187. // :_pht(pht)
  188. // ,_node(node)
  189. // {
  190. // }
  191. // //++
  192. // Self& operator++()//返回结构体对象
  193. // {
  194. // KeyOfT kot;
  195. // Hash hs;
  196. // size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
  197. // Node* cur = _pht->_tables[hashi];
  198. //
  199. // if (_node->_next)
  200. // {
  201. // _node = _node->_next;
  202. // }
  203. // else//该节点为空
  204. // {
  205. // while (hashi < _pht->_tables.size())
  206. // {
  207. // if (cur == nullptr)
  208. // {
  209. // ++hashi;
  210. // }
  211. // else
  212. // {
  213. // break;
  214. // }
  215. // }
  216. // if (hashi == _pht->_tables.size())
  217. // {
  218. // _node = nullptr;
  219. // }
  220. // else
  221. // {
  222. // _node = _pht->_tables[hashi];
  223. // }
  224. // }
  225. //
  226. // return *this;
  227. // }
  228. // //解引用*
  229. // T& operator*()
  230. // {
  231. // return _node->_data;
  232. // }
  233. // T* operator->()
  234. // {
  235. // return &_node->_data;
  236. // }
  237. // bool operator!=(const Self& s)
  238. // {
  239. // return _node != s._node;
  240. // }
  241. //};
  242. //哈希表
  243. template<class K, class T, class Hash , class KeyOfT>
  244. class HashTable
  245. {
  246. public:
  247. 友元声明(但是这种方式的代码过于冗余)
  248. //template<class K, class T, class Hash, class KeyOfT>
  249. //friend struct __HSIterator;
  250. //内部类
  251. template<class Ref, class Ptr>
  252. struct __HSIterator//这个是给hash表底层使用的迭代器对象
  253. {
  254. typedef HashNode<T> Node;
  255. typedef __HSIterator Self;
  256. const HashTable* _pht;
  257. Node* _node;
  258. __HSIterator( Node* node, const HashTable* pht)
  259. :_pht(pht)
  260. , _node(node)
  261. {
  262. }
  263. //++
  264. Self& operator++()//返回结构体对象
  265. {
  266. if (_node->_next)
  267. {
  268. _node = _node->_next;
  269. }
  270. else//该节点为空
  271. {
  272. KeyOfT kot;
  273. Hash hs;
  274. size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
  275. ++hashi;
  276. while (hashi < _pht->_tables.size())
  277. {
  278. if (_pht->_tables[hashi])
  279. break;
  280. ++hashi;
  281. }
  282. if (hashi == _pht->_tables.size())
  283. {
  284. _node = nullptr;
  285. }
  286. else
  287. {
  288. _node = _pht->_tables[hashi];
  289. }
  290. }
  291. return *this;
  292. }
  293. //解引用*
  294. Ref operator*()
  295. {
  296. return _node->_data;
  297. }
  298. Ptr operator->()
  299. {
  300. return &_node->_data;
  301. }
  302. bool operator!=(const Self& s)
  303. {
  304. return _node != s._node;
  305. }
  306. };
  307. typedef __HSIterator<T&, T*> iterator;
  308. iterator begin()
  309. {
  310. //找到第一个非空节点
  311. for (int i = 1;i< _tables.size(); ++i)
  312. {
  313. if (_tables[i])
  314. {
  315. return iterator(_tables[i], this);
  316. }
  317. }
  318. return end();
  319. }
  320. iterator end()
  321. {
  322. //直接是空
  323. return iterator(nullptr, this);
  324. }
  325. typedef HashNode<T> Node;
  326. public:
  327. HashTable()
  328. {
  329. _tables.resize(10,nullptr);
  330. }
  331. //插入
  332. pair<iterator, bool> Insert(const T& data)
  333. {
  334. Hash hs;//取模仿函数
  335. KeyOfT kot;//取key仿函数
  336. iterator it = Find(kot(data));
  337. if (it._node != nullptr)//存在节点
  338. {
  339. return make_pair(it, false);
  340. }
  341. //扩容
  342. if (_n == _tables.size())
  343. {
  344. vector<Node*> newTable(_tables.size() *2,nullptr);
  345. for (size_t i = 0; i<_tables.size(); ++i)
  346. {
  347. //首先,插入头节点
  348. Node* cur = _tables[i];
  349. //再处理后面的串
  350. while (cur)
  351. {
  352. Node* next = cur->_next;
  353. size_t hashi = hs(kot(cur->_data)) % newTable.size();
  354. //头插
  355. cur->_next = newTable[hashi];
  356. newTable[hashi] = cur;
  357. cur = next;
  358. }
  359. }
  360. _tables.swap(newTable);
  361. }
  362. size_t hashi = hs(kot(data)) % _tables.size();
  363. Node* newNode = new Node(data);
  364. newNode->_next = _tables[hashi];
  365. _tables[hashi] = newNode;
  366. ++_n;
  367. return make_pair(iterator(newNode, this), true);
  368. }
  369. //查找
  370. iterator Find(const K& key)
  371. {
  372. Hash hs;
  373. KeyOfT kot;
  374. size_t hashi = hs(key) % _tables.size();
  375. Node* cur = _tables[hashi];
  376. while (cur)
  377. {
  378. if (kot(cur->_data) == key)
  379. {
  380. return iterator(cur, this);
  381. }
  382. cur = cur->_next;
  383. }
  384. return iterator(nullptr, this);
  385. }
  386. size_t size()
  387. {
  388. return _n;
  389. }
  390. //删除
  391. bool Erase(const K& key)
  392. {
  393. KeyOfT kot;
  394. KeyOfT hs;
  395. Node* prev = nullptr;
  396. size_t hashi = hs(key) % _tables.size();
  397. Node* cur = _tables[hashi];
  398. while (cur)
  399. {
  400. if (kot(cur->_data) == key)
  401. {
  402. //如果是第一个节点
  403. if (prev == nullptr)
  404. {
  405. _tables[hashi] = cur->_next;
  406. }
  407. else
  408. {
  409. prev->_next = cur->_next;
  410. }
  411. delete cur;
  412. return true;
  413. }
  414. else
  415. {
  416. prev = cur;
  417. cur = cur->_next;
  418. }
  419. }
  420. return false;
  421. }
  422. private:
  423. vector<Node*> _tables;
  424. size_t _n = 0;
  425. };
  426. //void test_hash_bucket1()
  427. //{
  428. // HashTable<string, int> hb;
  429. // hb.Insert({"asada",1});
  430. // hb.Insert({"DASDAS",1});
  431. // hb.Insert({"DASDAS",1});
  432. // hb.Insert({"DASDAS",1});
  433. // hb.Insert({"DASDAS",1});
  434. // hb.Insert({"DASDAS",1});
  435. // hb.Insert({"DASbAS",1});
  436. // hb.Insert({"HHDH",1});
  437. // //cout << hb.Erase("") << endl;
  438. // cout << hb.size() << endl;
  439. // cout << hb.Find("asada") << endl;
  440. // cout << hb.Find("DASDAS") << endl;
  441. // cout << hb.Find("HHDH") << endl;
  442. //
  443. //}
  444. //void test_hash_bucket2()
  445. //{
  446. // vector<int> v = {1,2,3,4,5,65,6,7,8,9,90,0,2,3,23,45,232};
  447. // HashTable<int, int> hb;
  448. // for (size_t i = 0; i<v.size(); ++i)
  449. // {
  450. // if (i == 9)
  451. // {
  452. // int j = 0;
  453. // }
  454. // hb.Insert(make_pair(v[i],v[i]));
  455. // }
  456. // cout << hb.size() << endl;
  457. //}
  458. }

2、unordered_map

  1. #pragma once
  2. #pragma once
  3. #include"HashTable.h"
  4. namespace my_unordered_map {
  5. template<class K, class V, class Hash = hashFunc<K> >
  6. class unordered_map
  7. {
  8. public:
  9. //仿函数d
  10. struct MapKeyOfT
  11. {
  12. const K& operator()(const pair<K, V>& kv)
  13. {
  14. return kv.first;
  15. }
  16. };
  17. typedef typename hash_bucket::HashTable<K, pair<const K, V>, Hash, 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. iterator find(const K& key)
  31. {
  32. return _ht.Find(key);
  33. }
  34. bool erase(const K& key)
  35. {
  36. return _ht.Erase(key);
  37. }
  38. //方括号重载
  39. V& operator[](const K& key)
  40. {
  41. pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));//V()为匿名对象
  42. return ret.first->second;
  43. }
  44. private:
  45. hash_bucket::HashTable<K, pair<const K, V>, Hash, MapKeyOfT> _ht;
  46. };
  47. void test_unordered_map()
  48. {
  49. unordered_map<int,int> um;
  50. um.insert({1,1});
  51. um.insert({2,1});
  52. um.insert({3,1});
  53. um.insert({4,1});
  54. um.insert({5,1});
  55. um.insert({6,1});
  56. um.find(1);
  57. //cout << um.find(2) << endl;
  58. //cout << um.find(200) << endl;
  59. }
  60. void test_unordered_map1()
  61. {
  62. unordered_map<int, int> um;
  63. um.insert({ 1,1 });
  64. um.insert({ 2,1 });
  65. um.insert({ 3,1 });
  66. um.insert({ 4,1 });
  67. um.insert({ 5,1 });
  68. um.insert({ 6,1 });
  69. um[7];
  70. um[8];
  71. um[9];
  72. um[10];
  73. um[11]++;
  74. unordered_map<int, int>::iterator it = um.begin();
  75. while (it != um.end())
  76. {
  77. cout << it->first << " ";
  78. ++it;
  79. }
  80. cout << endl;
  81. }
  82. void test_unordered_map2()
  83. {
  84. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
  85. "苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };
  86. unordered_map<string, int> countMap;
  87. for (auto& e : arr)
  88. {
  89. countMap[e]++;
  90. }
  91. for (auto e : countMap)
  92. {
  93. cout << e.first << ":" << e.second << endl;
  94. }
  95. cout << endl;
  96. }
  97. };

3、unordered_set

  1. #pragma once
  2. #include"HashTable.h"
  3. namespace my_unorded_set {
  4. template<class K, class Hash = hashFunc<K> >
  5. class unorded_set
  6. {
  7. public:
  8. struct SetKeyOfT
  9. {
  10. const K& operator()(const K& key)
  11. {
  12. return key;
  13. }
  14. };
  15. typedef typename hash_bucket::HashTable< K,const K, Hash, SetKeyOfT>::iterator iterator;
  16. iterator beigin()
  17. {
  18. return _ht.begin();
  19. }
  20. iterator end()
  21. {
  22. return _ht.end();
  23. }
  24. pair<iterator, bool> insert(const K& key)
  25. {
  26. return _ht.Insert(key);
  27. }
  28. iterator find(const K& key)
  29. {
  30. return _ht.Find(key);
  31. }
  32. bool erase(const K& key)
  33. {
  34. return _ht.Erase(key);
  35. }
  36. private:
  37. hash_bucket::HashTable<K, const K, Hash, SetKeyOfT> _ht;
  38. };
  39. void test_unordered_set()
  40. {
  41. unorded_set<int> us;
  42. us.insert(1);
  43. us.insert(2);
  44. us.insert(3);
  45. us.insert(4);
  46. us.insert(6);
  47. us.insert(7);
  48. //cout << us.find(1) << endl;
  49. //cout << us.find(100) << endl;
  50. cout << "//" << endl;
  51. cout << us.erase(100) << endl;
  52. cout << us.erase(1) << endl;
  53. }
  54. void test_unordered_set1()
  55. {
  56. unorded_set<int> us;
  57. us.insert(99);
  58. us.insert(4);
  59. us.insert(6);
  60. us.insert(7);
  61. us.insert(9);
  62. us.insert(2);
  63. us.insert(3);
  64. us.insert(11);
  65. us.insert(1);
  66. us.insert(96);
  67. us.insert(16);
  68. us.insert(56);
  69. us.insert(50);
  70. us.insert(57);
  71. us.insert(58);
  72. us.erase(11);
  73. unorded_set<int>::iterator it = us.beigin();
  74. while (it != us.end())
  75. {
  76. cout << *it << " ";
  77. ++it;
  78. }
  79. cout << endl;
  80. }
  81. };

4、test

  1. #include"unordered_set.h"
  2. #include"unordered_map.h"
  3. int main()
  4. {
  5. //hash_bucket::test_hash_bucket1();
  6. cout << "unordered_set" << ":";
  7. my_unorded_set::test_unordered_set1();
  8. cout << "unordered_map" << ":";
  9. my_unordered_map::test_unordered_map1();
  10. cout << "unordered_map方括号重载测试" << ":" << endl;;
  11. my_unordered_map::test_unordered_map2();
  12. return 0;
  13. }

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

闽ICP备14008679号