当前位置:   article > 正文

C++:哈希,unordered_map和unordered_set_unordered map时间复杂度

unordered map时间复杂度

目录

一.unordered_map和unordered_set

1.时间复杂度:它们查找的时间复杂度平均都是O(1)

2.它们的底层结构相同,都使用哈希桶

简单的使用代码:

二.哈希

1. 直接定址法--(数分布集中 常用)

2. 除留余数法--(数分布不集中,均匀 常用)

3.哈希冲突

(2)闭散列

线性探测:

添加状态

二次探测

4.负载因子

5.哈希冲突的处理方法和哈希函数 区分

阶段一:只有插入的哈希

阶段二:完善哈希表,雏形哈希桶

1.当key是string或其他类型,如何映射?

2.仿函数中的特化

3.开散列(哈希桶)

阶段三:完善哈希桶

析构函数

优化insert

总代码:

阶段四:模拟实现unorderedmap/set

(1)template 第二个模板T

(2)class KeyOfT模板参数:

(3)HashFunc模板 要放到 UnorderedSet.h / UnorderedMap.h 中

UnorderedSet.h

UnorderedMap.h

HashTable.h

Test.cpp


一.unordered_map和unordered_set

unordered_map和unordered_set和map/set用法一样,不同就是unordered_map和unordered_set不排序,map/set自动排序

1.时间复杂度:它们查找的时间复杂度平均都是O(1)

哈希是通过哈希函数来计算元素的存储位置的,找的时候同样通过哈希函数找元素位 置,不需要循环遍历因此时间复杂度为O(1)

2.它们的底层结构相同,都使用哈希桶

3.它们在进行元素插入时,不需要比较key找待插入元素的位置,只需要通过哈希函数,就可以确认元素需要存储的位置。

简单的使用代码:

  1. #include<iostream>
  2. #include <set>
  3. #include <map>
  4. #include <unordered_set>
  5. #include <unordered_map>
  6. #include <time.h>
  7. using namespace std;
  8. void test_set()
  9. {
  10. unordered_set<int> s;
  11. //set<int> s;
  12. s.insert(2);
  13. s.insert(3);
  14. s.insert(1);
  15. s.insert(2);
  16. s.insert(5);
  17. //unordered_set<int>::iterator it = s.begin();
  18. auto it = s.begin();
  19. while (it != s.end())
  20. {
  21. cout << *it << " ";
  22. ++it;
  23. }
  24. cout << endl;
  25. for (auto e : s)
  26. {
  27. cout << e << " ";
  28. }
  29. cout << endl;
  30. }
  31. void test_op() //用于记录插入1000w个数两个容器花费时间的函数
  32. {
  33. int n = 10000000;
  34. vector<int> v;
  35. v.reserve(n);
  36. srand(time(0));
  37. for (int i = 0; i < n; ++i)
  38. {
  39. //v.push_back(i);
  40. //v.push_back(rand()+i); // 重复少
  41. v.push_back(rand()); // 重复多
  42. }
  43. size_t begin1 = clock();
  44. set<int> s;
  45. for (auto e : v)
  46. {
  47. s.insert(e);
  48. }
  49. size_t end1 = clock();
  50. size_t begin2 = clock();
  51. unordered_set<int> us;
  52. for (auto e : v)
  53. {
  54. us.insert(e);
  55. }
  56. size_t end2 = clock();
  57. cout << s.size() << endl;
  58. cout << "set insert:" << end1 - begin1 << endl;
  59. cout << "unordered_set insert:" << end2 - begin2 << endl;
  60. size_t begin3 = clock();
  61. for (auto e : v)
  62. {
  63. s.find(e);
  64. }
  65. size_t end3 = clock();
  66. size_t begin4 = clock();
  67. for (auto e : v)
  68. {
  69. us.find(e);
  70. }
  71. size_t end4 = clock();
  72. cout << "set find:" << end3 - begin3 << endl;
  73. cout << "unordered_set find:" << end4 - begin4 << endl;
  74. size_t begin5 = clock();
  75. for (auto e : v)
  76. {
  77. s.erase(e);
  78. }
  79. size_t end5 = clock();
  80. size_t begin6 = clock();
  81. for (auto e : v)
  82. {
  83. us.erase(e);
  84. }
  85. size_t end6 = clock();
  86. cout << "set erase:" << end5 - begin5 << endl;
  87. cout << "unordered_set erase:" << end6 - begin6 << endl;
  88. }
  89. void test_map()
  90. {
  91. unordered_map<string, string> dict;
  92. dict.insert(make_pair("sort", "排序"));
  93. dict.insert(make_pair("left", "左边"));
  94. dict.insert(make_pair("left", "剩余"));
  95. dict["string"];
  96. dict["left"] = "剩余";
  97. dict["string"] = "字符串";
  98. }
  99. int main()
  100. {
  101. //test_set();
  102. //test_op();
  103. test_map();
  104. return 0;
  105. }

二.哈希

哈希(散列)-映射:存储关键字跟存储位置建立关联关系

值和存储位置——> 映射关系 ——> 查找按映射关系去找

哈希是一种用来进行高效查找的数据结构,查找的时间复杂度平均为O(1)

采用哈希方式解决问题时,必须使用哈希函数

哈希查找的时间复杂度不一定是O(1)(因为存在哈希冲突,一般基本都是O(1))

哈希是以牺牲空间为代价,提高查询的效率(采用哈希处理时,一般所需空间都会比元素个数多,否则产生冲突的概率就比较大,影 响哈希的性能)

直接建立映射关系问题:
1、数据范围分布很广、不集中(除留余数法解决
2、key的数据不是整数,是字符串怎么办?是自定义类型对象怎么办?

1. 直接定址法--(数分布集中 常用)

取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B
优点:简单、均匀
缺点:需要事先知道关键字的分布情况
使用场景:适合查找比较小且集中连续的情况

2. 除留余数法--(数分布不集中,均匀 常用)

key %表大小——>映射的位置
                         下面都假设这个表大小是10

 除留余数法会有个问题,就是哈希冲突

3.哈希冲突

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

例如:除留余数法中,20%表大小10=0,把20放在下标0处,如果有30,50呢,30,50%10也得放在下标0处,所以要解决哈希冲突,解决哈希冲突两种常见的方法是:闭散列开散列。

(2)闭散列

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

线性探测:

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

hash(key)%N + i(i= 0,1,2,3...):比如10%N(N是表大小10)=0,因为下标0已经放了20,则再加i=1,10%10+1=1,就把10放到了下标是1的地方。30:放30就是30%10+2=2,把30放到了下标是2的地方
 

 缺点:我占你的,你占他的,拥堵

添加状态

比如查找50时,一直找到空就停止,但是如果在50之前有删除的数据,就会在删除数据的位置停下,这样就找不到50了,所以添加个状态用于区分空和删除的位置。遇到空EMPTY就停,遇到删除DELETE和存在 EXITS就不停

  1. enum State
  2. {
  3. EMPTY,
  4. EXITS,
  5. DELETE
  6. };

二次探测

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

hash(key)%N +  (i= 0,1,2,3...)

比如10%N(N是表大小10)=0,因为下标0已经放了20,则再加 =1,10%10+1=1,就把10放到了下标是1的地方。30:放30就是30%10+2²=4,把30放到了下标是4的地方

对上面方法的优化:不那么拥堵

starti %= _tables.size();还是.capacity() ?

_tables.size()是能存数据的位置个数,_tables.capacity()是总容量大小(包括没有初始化的空间)类似resize和reserve的区别,starti %= _tables.size(); 插入数据只能插在能插入的位置,因为_tables[hashi] 的operator[] 会自动检查这个位置是否有值还是没有值的随机数,有值就可以插入。

4.负载因子

负载因子(散列表的载荷因子α) = 填入表中的元素个数 / 哈希表的长度

由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大,表明填入表中的元素越多,产生冲突的可能性就越大:反之,α越小,标明填入表中的元素越少,产生冲突的可能性就越小。

哈希表什么情况下进行扩容?如何扩容?
负载因子大于0.7就扩容。

5.哈希冲突的处理方法和哈希函数 区分

 哈希函数作用是:建立元素与其存储位置之前的对应关系的,在存储元素时,先通过哈希函数计算元素在哈希表格中的存储位置,然后存储元素。好的哈希函数可以减少冲突的概率,但是不能绝对避免哈希函数,万一发生哈希冲突,得需要借助哈希冲突处理方法来 解决。

   常见的哈希函数有:直接定址法、除留余数法、平方取中法、随机数法、数字分析法、叠加法等

   常见哈希冲突处理:闭散列(线性探测、二次探测)、开散列(链地址法)、多次散列

常见哈希函数
1. 直接定址法--(常用)
取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B
优点:简单、均匀
缺点:需要事先知道关键字的分布情况
使用场景:适合查找比较小且连续的情况
2. 除留余数法--(常用)
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,
按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址
3. 平方取中法--(了解)
假设关键字为1234,对它平方就是1522756,抽取中间的3位227作为哈希地址;
再比如关键字为4321,对它平方就是18671041,抽取中间的3位671(或710)作为哈希地址
平方取中法比较适合:不知道关键字的分布,而位数又不是很大的情况
4. 折叠法--(了解)
折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分位数可以短些),然后将这
几部分叠加求和,并按散列表表长,取后几位作为散列地址。
折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
5. 随机数法--(了解)
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key) = random(key),其中
random为随机数函数。
通常应用于关键字长度不等时采用此法
6. 数学分析法--(了解)
设有n个d位数,每一位可能有r种不同的符号,这r种不同的符号在各位上出现的频率不一定
相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只
有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散
列地址。

阶段一:只有插入的哈希

  1. #pragma once
  2. #include<vector>
  3. enum State
  4. {
  5. EMPTY,
  6. EXITS,
  7. DELETE
  8. };
  9. // 休息21:16继续
  10. template<class K, class V>
  11. struct HashData
  12. {
  13. pair<K, V> _kv;
  14. State _state;
  15. };
  16. template<class K, class V>
  17. class HashTable
  18. {
  19. public:
  20. bool Insert(const pair<K, V>& kv)
  21. {
  22. // 负载因子到0.7及以上,就扩容。这里算 负载因子>0.7,因为是小数,前面可能需要类型转换,否则除出来是0,我们不如把 负载因子*10>7
  23. if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
  24. {
  25. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  26. // 扩容以后,需要重新映射
  27. HashTable<K, V> newHT;
  28. newHT._tables.resize(newSize);
  29. // 遍历旧表,插入newHT
  30. for (auto& e : _tables)
  31. {
  32. if (e._state == EXITS)
  33. {
  34. newHT.Insert(e._kv);
  35. }
  36. }
  37. newHT._tables.swap(_tables);
  38. }
  39. size_t starti = kv.first;
  40. starti %= _tables.size();
  41. size_t hashi = starti;
  42. size_t i = 1;
  43. // 线性探测/二次探测
  44. while (_tables[hashi]._state == EXITS)
  45. {
  46. hashi = starti + i;
  47. ++i;
  48. hashi %= _tables.size();
  49. }
  50. _tables[hashi]._kv = kv;
  51. _tables[hashi]._state = EXITS;
  52. _n++;
  53. }
  54. HashData* Find(const K& key);
  55. bool Erase(const K& key);
  56. private:
  57. vector<HashData> _tables;
  58. size_t _n = 0; // 存储关键字个数
  59. };

阶段二:完善哈希表,雏形哈希桶

1.当key是string或其他类型,如何映射?

key是string或者自定义类型时,需要转成整数再映射进哈希表,这个转成整数的方法有多样,比如:string类型转成整数方法是BKDR法,hash乘一个值131再加字符串的ASCII值,这样能减少冲突。如果仅仅是通过整个字符串的ASCII值来来映射,比如"abc"和"bac"就一样,一样就是哈希冲突,哈希冲突也没关系,我们有处理哈希冲突的机制:把映射到同一位置的值像下一个位置放(线性探测)。类似于先放20后再放10,映射在大小是10的哈希表中,都放在0处,哈希冲突了,就可以把10放在20后面的空位置上。

  1. template<>
  2. struct DefaultHash<string>
  3. {
  4. size_t operator()(const string& key)
  5. { //key是string类型用仿函数DefaultHash转成整数
  6. //转换方法:BKDR ,数学研究出来的
  7. size_t hash = 0;
  8. for (auto ch : key)
  9. {
  10. hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
  11. }
  12. return hash;
  13. }
  14. };

2.仿函数中的特化

这样模拟实现unorodered_map时,直接定义 unorodered_map<int,int> a; 时不用传仿函数是因为template<class K, class V, class HashFunc = DefaultHash<K>> 仿函数给了缺省参数,不传默认是基础的类struct DefaultHash,直接定义 unorodered_map<string,string> b; 时也可以不用传仿函数是因为类模板特化,key是string就会使用特化版本的struct DefaultHash<string>

  1. template<class K>
  2. struct DefaultHash
  3. {
  4. size_t operator()(const K& key)
  5. {
  6. return (size_t)key;
  7. }
  8. };
  9. template<>
  10. struct DefaultHash<string>
  11. {
  12. size_t operator()(const string& key)
  13. { //key是string类型用仿函数DefaultHash转成整数
  14. //转换方法:BKDR ,数学研究出来的
  15. size_t hash = 0;
  16. for (auto ch : key)
  17. {
  18. hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
  19. }
  20. return hash;
  21. }
  22. };
  23. template<class K, class V, class HashFunc = DefaultHash<K>>
  24. class HashTable
  25. {
  26. typedef HashData<K, V> Data;
  27. public:
  28. ……

3.开散列(哈希桶)

(1) 哈希桶/开散列/链地址法/开链法 概念
开散列法又叫 哈希桶/链地址法/开链法,数据不存在表中,表里面存储一个链表指针,冲突的数据链表形式挂起来。 哈希桶用 单链表,不进行插入删除操作就不用双向链表,仅头插就可以,而且单链表比双向链表少存一个指针,能省则省。

vector<Node*> 数组每个位置存的是链表第一个值的指针

  1. #pragma once
  2. #include<vector>
  3. namespace CloseHash
  4. {
  5. enum State
  6. {
  7. EMPTY,
  8. EXITS,
  9. DELETE
  10. };
  11. template<class K, class V>
  12. struct HashData
  13. {
  14. pair<K, V> _kv;
  15. State _state = EMPTY;
  16. };
  17. template<class K>
  18. struct DefaultHash
  19. {
  20. size_t operator()(const K& key)
  21. {
  22. return (size_t)key;
  23. }
  24. };
  25. template<>
  26. struct DefaultHash<string>
  27. {
  28. size_t operator()(const string& key)
  29. { //key是string类型用仿函数DefaultHash转成整数
  30. //转换方法:BKDR ,数学研究出来的
  31. size_t hash = 0;
  32. for (auto ch : key)
  33. {
  34. hash = hash * 131 + ch; //hash乘一个值131再加字符串的ASCII
  35. }
  36. return hash;
  37. }
  38. };
  39. //struct StringHash
  40. //{
  41. // // "abcd"
  42. // // "aa"
  43. // size_t operator()(const string& key)
  44. // {
  45. // //return key[0]; 可以
  46. // //return (size_t)&key; 不行
  47. //
  48. // /*size_t hash = 0;
  49. // for (auto ch : key)
  50. // {
  51. // hash += ch;
  52. // }
  53. //
  54. // return hash;*/
  55. //
  56. // // BKDR
  57. // size_t hash = 0;
  58. // for (auto ch : key)
  59. // {
  60. // hash = hash* 131 + ch;
  61. // }
  62. //
  63. // return hash;
  64. // }
  65. //};
  66. template<class K, class V, class HashFunc = DefaultHash<K>>
  67. class HashTable
  68. {
  69. typedef HashData<K, V> Data;
  70. public:
  71. bool Insert(const pair<K, V>& kv)
  72. {
  73. if (Find(kv.first)) //去冗余 易错点1,容易忘写
  74. {
  75. return false;
  76. }
  77. // 负载因子到0.7及以上,就扩容
  78. if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
  79. {
  80. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  81. // 扩容以后,需要重新映射
  82. HashTable<K, V, HashFunc> newHT;
  83. newHT._tables.resize(newSize);
  84. // 遍历旧表,插入newHT
  85. for (auto& e : _tables)
  86. {
  87. if (e._state == EXITS) //易错点2,容易忘写
  88. {
  89. newHT.Insert(e._kv);
  90. }
  91. }
  92. newHT._tables.swap(_tables);
  93. }
  94. HashFunc hf;
  95. size_t starti = hf(kv.first);
  96. starti %= _tables.size();
  97. size_t hashi = starti;
  98. size_t i = 1;
  99. // 线性探测/二次探测
  100. while (_tables[hashi]._state == EXITS)
  101. {
  102. hashi = starti + i;
  103. ++i;
  104. hashi %= _tables.size();
  105. }
  106. _tables[hashi]._kv = kv;
  107. _tables[hashi]._state = EXITS;
  108. _n++;
  109. return true;
  110. }
  111. Data* Find(const K& key)
  112. {
  113. if (_tables.size() == 0) //哈希表还没数据时不能查找
  114. {
  115. return nullptr;
  116. }
  117. HashFunc hf;
  118. size_t starti = hf(key); //不同类型的key通过对应的仿函数转成整数-
  119. starti %= _tables.size();//-比如key是string类型,就-
  120. size_t hashi = starti; //-用仿函数DefaultHash转成整形
  121. size_t i = 1;
  122. while (_tables[hashi]._state != EMPTY)
  123. { //下面如果状态是DELETE就无法被查找到
  124. if (_tables[hashi]._state != DELETE && _tables[hashi]._kv.first == key)
  125. {
  126. return &_tables[hashi];
  127. }
  128. hashi = starti + i;
  129. ++i;
  130. hashi %= _tables.size();
  131. }
  132. return nullptr;
  133. }
  134. bool Erase(const K& key)
  135. {
  136. Data* ret = Find(key);
  137. if (ret)
  138. {
  139. ret->_state = DELETE;
  140. --_n;
  141. return true;
  142. }
  143. else
  144. {
  145. return false;
  146. }
  147. }
  148. private:
  149. vector<Data> _tables;
  150. size_t _n = 0; // 存储关键字个数
  151. };
  152. void TestHT1()
  153. {
  154. int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
  155. //HashTable<int, int, DefaultHash<int>> ht;
  156. HashTable<int, int> ht;
  157. if (ht.Find(10))
  158. {
  159. cout << "找到了10" << endl;
  160. }
  161. for (auto e : a)
  162. {
  163. ht.Insert(make_pair(e, e));
  164. }
  165. // 测试扩容
  166. ht.Insert(make_pair(15, 15));
  167. ht.Insert(make_pair(5, 5));
  168. ht.Insert(make_pair(15, 15));
  169. if (ht.Find(50))
  170. {
  171. cout << "找到了50" << endl;
  172. }
  173. if (ht.Find(10))
  174. {
  175. cout << "找到了10" << endl;
  176. }
  177. ht.Erase(10);
  178. ht.Erase(10);
  179. if (ht.Find(50))
  180. {
  181. cout << "找到了50" << endl;
  182. }
  183. if (ht.Find(10))
  184. {
  185. cout << "找到了10" << endl;
  186. }
  187. }
  188. void TestHT2()
  189. {
  190. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
  191. /*string s1("苹果");
  192. string s2("果苹");
  193. string s3("果果");
  194. string s4("萍果");
  195. string s5("abcd");
  196. string s6("bcad");
  197. string s7("aadd");
  198. StringHash hf;
  199. cout << hf(s1) << endl;
  200. cout << hf(s2) << endl;
  201. cout << hf(s3) << endl;
  202. cout << hf(s4) << endl << endl;
  203. cout << hf(s5) << endl;
  204. cout << hf(s6) << endl;
  205. cout << hf(s7) << endl;*/
  206. //HashTable<string, int, StringHash> countHT;
  207. HashTable<string, int> countHT;
  208. for (auto& str : arr)
  209. {
  210. auto ret = countHT.Find(str);
  211. if (ret)
  212. {
  213. ret->_kv.second++;
  214. }
  215. else
  216. {
  217. countHT.Insert(make_pair(str, 1));
  218. }
  219. }
  220. // 对应类型配一个仿函数,仿函数对象实现把key对象转换成映射的整数
  221. //HashTable<Date, int, DateHash> countHT;
  222. //HashTable<Student, int, StudentHash> countHT;
  223. HashTable<string, int> copy(countHT);
  224. }
  225. }
  226. namespace Bucket
  227. {
  228. template<class K, class V>
  229. struct HashNode
  230. {
  231. pair<K, V> _kv;
  232. HashNode<K, V>* _next;
  233. HashNode(const pair<K, V>& kv)
  234. :_kv(kv)
  235. , _next(nullptr)
  236. {}
  237. };
  238. template<class K, class V>
  239. class HashTable
  240. {
  241. typedef HashNode<K, V> Node;
  242. public:
  243. bool Insert(const pair<K, V>& kv)
  244. {
  245. if (Find(kv.first))
  246. {
  247. return false;
  248. }
  249. // 负载因子 == 1 扩容
  250. if (_tables.size() == _n)
  251. {
  252. // 扩容,有缺陷,可以再优化,大家下去可以思考一下
  253. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  254. HashTable<K, V> newHT;
  255. newHT._tables.resize(newSize, nullptr);
  256. for (size_t i = 0; i < _tables.size(); ++i)
  257. {
  258. Node* cur = _tables[i];
  259. while (cur)
  260. {
  261. newHT.Insert(cur->_kv);
  262. cur = cur->_next;
  263. }
  264. }
  265. newHT._tables.swap(_tables);
  266. }
  267. size_t hashi = kv.first;
  268. hashi %= _tables.size();
  269. // 头插到对应的桶即可
  270. Node* newnode = new Node(kv);
  271. newnode->_next = _tables[hashi];
  272. _tables[hashi] = newnode;
  273. ++_n;
  274. return true;
  275. }
  276. Node* Find(const K& key)
  277. {
  278. if (_tables.size() == 0)
  279. {
  280. return nullptr;
  281. }
  282. size_t hashi = key;
  283. hashi %= _tables.size();
  284. Node* cur = _tables[hashi];
  285. while (cur)
  286. {
  287. if (cur->_kv.first == key)
  288. {
  289. return cur;
  290. }
  291. cur = cur->_next;
  292. }
  293. return nullptr;
  294. }
  295. private:
  296. // 指针数组
  297. vector<Node*> _tables;
  298. size_t _n = 0;
  299. };
  300. void TestHT1()
  301. {
  302. int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
  303. //HashTable<int, int, DefaultHash<int>> ht;
  304. HashTable<int, int> ht;
  305. if (ht.Find(10))
  306. {
  307. cout << "找到了10" << endl;
  308. }
  309. for (auto e : a)
  310. {
  311. ht.Insert(make_pair(e, e));
  312. }
  313. // 测试扩容
  314. ht.Insert(make_pair(15, 15));
  315. ht.Insert(make_pair(5, 5));
  316. ht.Insert(make_pair(15, 15));
  317. ht.Insert(make_pair(25, 15));
  318. ht.Insert(make_pair(35, 15));
  319. ht.Insert(make_pair(45, 15));
  320. }
  321. }

阶段三:完善哈希桶

新增析构函数,优化版insert,删除Erase,拷贝构造,赋值(自己写),Hashfunc(string也可以%)

析构函数

vector是自定义类型会去调用自己的析构函数析构数组,但不会释放每个位置上的链表的节点,我们要一个一个释放每个链表的每个节点

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

优化insert

开一个新的扩容后大小的数组newTable,把原数组_table的节点一个一个头插到新数组中,这样就避免原方法中的双重消耗:新开节点(插入数组),释放旧数组节点

  1. bool Insert(const T& data)
  2. {
  3. HashFunc hf;
  4. KeyOfT kot;
  5. if (Find(kot(data)))
  6. {
  7. return false;
  8. }
  9. // 负载因子 == 1 扩容
  10. if (_tables.size() == _n)
  11. {
  12. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  13. vector<Node*> newTable; //开一个新的扩容后大小的数组
  14. newTable.resize(newSize, nullptr); //开一个新的扩容后大小的数组
  15. for (size_t i = 0; i < _tables.size(); ++i)
  16. {
  17. Node* cur = _tables[i];
  18. while (cur)
  19. {
  20. Node* next = cur->_next;
  21. size_t hashi = hf(kot(cur->_data)) % newSize;
  22. cur->_next = newTable[hashi];
  23. newTable[hashi] = cur;
  24. cur = next;
  25. }
  26. _tables[i] = nullptr;
  27. }
  28. newTable.swap(_tables);
  29. }
  30. size_t hashi = hf(kot(data));
  31. hashi %= _tables.size();
  32. // 头插到对应的桶即可
  33. Node* newnode = new Node(data);
  34. newnode->_next = _tables[hashi];
  35. _tables[hashi] = newnode;
  36. ++_n;
  37. return true;
  38. }

总代码:

  1. #pragma once
  2. #include<vector>
  3. template<class K>
  4. struct DefaultHash
  5. {
  6. size_t operator()(const K& key)
  7. {
  8. return (size_t)key;
  9. }
  10. };
  11. template<>
  12. struct DefaultHash<string>
  13. {
  14. size_t operator()(const string& key)
  15. {
  16. // BKDR
  17. size_t hash = 0;
  18. for (auto ch : key)
  19. {
  20. hash = hash * 131 + ch;
  21. }
  22. return hash;
  23. }
  24. };
  25. namespace Bucket
  26. {
  27. template<class K, class V>
  28. struct HashNode
  29. {
  30. pair<K, V> _kv;
  31. HashNode<K, V>* _next;
  32. HashNode(const pair<K, V>& kv)
  33. :_kv(kv)
  34. , _next(nullptr)
  35. {}
  36. };
  37. template<class K, class V, class HashFunc = DefaultHash<K>>
  38. class HashTable
  39. {
  40. typedef HashNode<K, V> Node;
  41. public:
  42. ~HashTable()
  43. {
  44. for (size_t i = 0; i < _tables.size(); ++i)
  45. {
  46. Node* cur = _tables[i];
  47. while (cur)
  48. {
  49. Node* next = cur->_next;
  50. delete cur;
  51. cur = next;
  52. }
  53. _tables[i] = nullptr;
  54. }
  55. }
  56. bool Insert(const pair<K, V>& kv)
  57. {
  58. if (Find(kv.first))
  59. {
  60. return false;
  61. }
  62. HashFunc hf;
  63. // 负载因子 == 1 扩容
  64. if (_tables.size() == _n)
  65. {
  66. // 扩容,有缺陷,可以再优化,大家下去可以思考一下
  67. /*size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  68. HashTable<K, V> newHT;
  69. newHT._tables.resize(newSize, nullptr);
  70. for (size_t i = 0; i < _tables.size(); ++i)
  71. {
  72. Node* cur = _tables[i];
  73. while (cur)
  74. {
  75. newHT.Insert(cur->_kv);
  76. cur = cur->_next;
  77. }
  78. }
  79. newHT._tables.swap(_tables);*/
  80. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
  81. vector<Node*> newTable;
  82. newTable.resize(newSize, nullptr);
  83. for (size_t i = 0; i < _tables.size(); ++i)
  84. {
  85. Node* cur = _tables[i];
  86. while (cur)
  87. {
  88. Node* next = cur->_next;
  89. size_t hashi = hf(cur->_kv.first) % newSize;
  90. cur->_next = newTable[hashi];
  91. newTable[hashi] = cur;
  92. cur = next;
  93. }
  94. _tables[i] = nullptr;
  95. }
  96. newTable.swap(_tables);
  97. }
  98. size_t hashi = hf(kv.first);
  99. hashi %= _tables.size();
  100. // 头插到对应的桶即可
  101. Node* newnode = new Node(kv);
  102. newnode->_next = _tables[hashi];
  103. _tables[hashi] = newnode;
  104. ++_n;
  105. return true;
  106. }
  107. Node* Find(const K& key)
  108. {
  109. if (_tables.size() == 0)
  110. {
  111. return nullptr;
  112. }
  113. HashFunc hf;
  114. size_t hashi = hf(key);
  115. //size_t hashi = HashFunc()(key);
  116. hashi %= _tables.size();
  117. Node* cur = _tables[hashi];
  118. while (cur)
  119. {
  120. if (cur->_kv.first == key)
  121. {
  122. return cur;
  123. }
  124. cur = cur->_next;
  125. }
  126. return nullptr;
  127. }
  128. bool Erase(const K& key)
  129. {
  130. if (_tables.size() == 0)
  131. {
  132. return false;
  133. }
  134. HashFunc hf;
  135. size_t hashi = hf(key);
  136. hashi %= _tables.size();
  137. Node* prev = nullptr;
  138. Node* cur = _tables[hashi];
  139. while (cur)
  140. {
  141. if (cur->_kv.first == key)
  142. {
  143. if (prev == nullptr)
  144. {
  145. _tables[hashi] = cur->_next;
  146. }
  147. else
  148. {
  149. prev->_next = cur->_next;
  150. }
  151. delete cur;
  152. return true;
  153. }
  154. prev = cur;
  155. cur = cur->_next;
  156. }
  157. return false;
  158. //size_t hashi = key;
  159. //hashi %= _tables.size();
  160. //Node* cur = _tables[hashi];
  161. //while (cur)
  162. //{
  163. // if (cur->_kv.first == key)
  164. // {
  165. // if (cur->_next == nullptr)
  166. // {
  167. // cur->_kv = _tables[hashi]->_kv;
  168. // Node* first = _tables[hashi];
  169. // _tables[hashi] = first->_next;
  170. // delete first;
  171. // }
  172. // else
  173. // {
  174. // //....
  175. // }
  176. // return true;
  177. // }
  178. // prev = cur;
  179. // cur = cur->_next;
  180. //}
  181. //return false;
  182. }
  183. private:
  184. // 指针数组
  185. vector<Node*> _tables;
  186. size_t _n = 0;
  187. };
  188. void TestHT1()
  189. {
  190. int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
  191. //HashTable<int, int, DefaultHash<int>> ht;
  192. HashTable<int, int> ht;
  193. if (ht.Find(10))
  194. {
  195. cout << "找到了10" << endl;
  196. }
  197. for (auto e : a)
  198. {
  199. ht.Insert(make_pair(e, e));
  200. }
  201. ht.Erase(20);
  202. ht.Erase(10);
  203. ht.Erase(30);
  204. ht.Erase(50);
  205. // 测试扩容
  206. ht.Insert(make_pair(15, 15));
  207. ht.Insert(make_pair(5, 5));
  208. ht.Insert(make_pair(15, 15));
  209. ht.Insert(make_pair(25, 15));
  210. ht.Insert(make_pair(35, 15));
  211. ht.Insert(make_pair(45, 15));
  212. }
  213. void TestHT2()
  214. {
  215. int a[] = { 20, 5, 8, 99999, 10, 30, 50 };
  216. HashTable<int, int> ht;
  217. for (auto e : a)
  218. {
  219. ht.Insert(make_pair(e, e));
  220. }
  221. // 需要自己实现拷贝构造,完成链表桶深拷贝
  222. //HashTable<int, int> copy(ht);
  223. }
  224. void TestHT3()
  225. {
  226. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
  227. //HashTable<string, int, StringHash> countHT;
  228. HashTable<string, int> countHT;
  229. for (auto& str : arr)
  230. {
  231. auto ret = countHT.Find(str);
  232. if (ret)
  233. {
  234. ret->_kv.second++;
  235. }
  236. else
  237. {
  238. countHT.Insert(make_pair(str, 1));
  239. }
  240. }
  241. }
  242. }

阶段四:模拟实现unorderedmap/set

(1)template<class K, class T, class KeyOfT, class HashFunc> 第二个模板T

作用是让同一份哈希桶的代码能区分unordered_set和unordered_map,如果是unordered_set模板T就传入K,unordered_map模板T就传入pair

(2)class KeyOfT模板参数:

用于取出map中的pair中的K,取set中的key

  1. KeyOfT kot;
  2. if (Find(kot(data)))
  3. {
  4. return false;
  5. }

(3)HashFunc模板 要放到 UnorderedSet.h / UnorderedMap.h 中

因为K可能是 Date 这些自定义类型,无法直接比较,要写个仿函数DateHash,test_set() 中定义对象时就传这个仿函数unordered_set<Date, DateHash> sd;

UnorderedSet.h

  1. pragma once
  2. #include "HashTable.h"
  3. namespace bit
  4. {
  5. // 21:06
  6. template<class K, class HashFunc = DefaultHash<K>>
  7. class unordered_set
  8. {
  9. struct SetKeyOfT
  10. {
  11. const K& operator()(const K& key)
  12. {
  13. return key;
  14. }
  15. };
  16. public:
  17. typedef typename Bucket::HashTable<K, K, SetKeyOfT, HashFunc>::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 K& key)
  27. {
  28. return _ht.Insert(key);
  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. private:
  39. Bucket::HashTable<K, K, SetKeyOfT, HashFunc> _ht;
  40. };
  41. struct Date
  42. {
  43. Date(int year = 1, int month = 1, int day = 1)
  44. :_year(year)
  45. , _month(month)
  46. , _day(day)
  47. {}
  48. bool operator==(const Date& d) const
  49. {
  50. return _year == d._year
  51. && _month == d._month
  52. && _day == d._day;
  53. }
  54. int _year;
  55. int _month;
  56. int _day;
  57. };
  58. struct DateHash
  59. {
  60. size_t operator()(const Date& d)
  61. {
  62. //return d._year + d._month + d._day;
  63. size_t hash = 0;
  64. hash += d._year;
  65. hash *= 131;
  66. hash += d._month;
  67. hash *= 1313;
  68. hash += d._day;
  69. //cout << hash << endl;
  70. return hash;
  71. }
  72. };
  73. void test_set()
  74. {
  75. unordered_set<int> s;
  76. //set<int> s;
  77. s.insert(2);
  78. s.insert(3);
  79. s.insert(1);
  80. s.insert(2);
  81. s.insert(5);
  82. s.insert(12);
  83. //unordered_set<int>::iterator it = s.begin();
  84. unordered_set<int>::iterator it;
  85. it = s.begin();
  86. //auto it = s.begin();
  87. while (it != s.end())
  88. {
  89. cout << *it << " ";
  90. ++it;
  91. }
  92. cout << endl;
  93. for (auto e : s)
  94. {
  95. cout << e << " ";
  96. }
  97. cout << endl;
  98. unordered_set<Date, DateHash> sd;
  99. sd.insert(Date(2022, 3, 4));
  100. sd.insert(Date(2022, 4, 3));
  101. }
  102. }

UnorderedMap.h

  1. #pragma once
  2. #include "HashTable.h"
  3. namespace bit
  4. {
  5. template<class K, class V, class HashFunc = DefaultHash<K>>
  6. class unordered_map
  7. {
  8. struct MapKeyOfT
  9. {
  10. const K& operator()(const pair<K, V>& kv)
  11. {
  12. return kv.first;
  13. }
  14. };
  15. public:
  16. typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc>::iterator iterator;
  17. iterator begin()
  18. {
  19. return _ht.begin();
  20. }
  21. iterator end()
  22. {
  23. return _ht.end();
  24. }
  25. pair<iterator, bool> insert(const pair<K, V>& kv)
  26. {
  27. return _ht.Insert(kv);
  28. }
  29. iterator find(const K& key)
  30. {
  31. return _ht.Find(key);
  32. }
  33. bool erase(const K& key)
  34. {
  35. return _ht.Erase(key);
  36. }
  37. V& operator[](const K& key)
  38. {
  39. pair<iterator, bool> ret = insert(make_pair(key, V()));
  40. return ret.first->second;
  41. }
  42. private:
  43. Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> _ht;
  44. };
  45. void test_map()
  46. {
  47. unordered_map<string, string> dict;
  48. dict.insert(make_pair("sort", ""));
  49. dict.insert(make_pair("left", ""));
  50. dict.insert(make_pair("left", "ʣ"));
  51. dict["string"];
  52. dict["left"] = "ʣ";
  53. dict["string"] = "ַ";
  54. unordered_map<string, string>::iterator it = dict.begin();
  55. while (it != dict.end())
  56. {
  57. cout << it->first << " " << it->second << endl;
  58. ++it;
  59. }
  60. cout << endl;
  61. for (auto& kv : dict)
  62. {
  63. cout << kv.first << " " << kv.second << endl;
  64. }
  65. }
  66. }

HashTable.h

  1. #pragma once
  2. #include<vector>
  3. template<class K>
  4. struct DefaultHash
  5. {
  6. size_t operator()(const K& key)
  7. {
  8. return (size_t)key;
  9. }
  10. };
  11. template<>
  12. struct DefaultHash<string>
  13. {
  14. size_t operator()(const string& key)
  15. {
  16. // BKDR
  17. size_t hash = 0;
  18. for (auto ch : key)
  19. {
  20. hash = hash * 131 + ch;
  21. }
  22. return hash;
  23. }
  24. };
  25. namespace Bucket
  26. {
  27. template<class T>
  28. struct HashNode
  29. {
  30. T _data;
  31. HashNode<T>* _next;
  32. HashNode(const T& data)
  33. :_data(data)
  34. , _next(nullptr)
  35. {}
  36. };
  37. template<class K, class T, class KeyOfT, class HashFunc>
  38. class HashTable;
  39. template<class K, class T, class KeyOfT, class HashFunc>
  40. class __HTIterator
  41. {
  42. typedef HashNode<T> Node;
  43. typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;
  44. public:
  45. Node* _node;
  46. HashTable<K, T, KeyOfT, HashFunc>* _pht;
  47. __HTIterator() {} //默认构造函数和非默认构造函数重载
  48. __HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
  49. :_node(node)
  50. , _pht(pht)
  51. {}
  52. Self& operator++()
  53. {
  54. if (_node->_next)
  55. {
  56. _node = _node->_next;
  57. }
  58. else
  59. {
  60. KeyOfT kot;
  61. HashFunc hf;
  62. size_t hashi = hf(kot(_node->_data)) % _pht->_tables.size();
  63. ++hashi;
  64. //找下一个不为空的桶
  65. for (; hashi < _pht->_tables.size(); ++hashi)
  66. {
  67. if (_pht->_tables[hashi])
  68. {
  69. _node = _pht->_tables[hashi];
  70. break;
  71. }
  72. }
  73. // 没有找到不为空的桶,用nullptr去做end标识
  74. if (hashi == _pht->_tables.size())
  75. {
  76. _node = nullptr;
  77. }
  78. }
  79. return *this;
  80. }
  81. T& operator*()
  82. {
  83. return _node->_data;
  84. }
  85. T* operator->()
  86. {
  87. return &_node->_data;
  88. }
  89. bool operator!=(const Self& s) const
  90. {
  91. return _node != s._node;
  92. }
  93. bool operator==(const Self& s) const
  94. {
  95. return _node == s._node;
  96. }
  97. };
  98. // unordered_map ->HashTable<K, pair<K, V>, MapKeyOfT> _ht;
  99. // unordered_set ->HashTable<K, K, SetKeyOfT> _ht;
  100. template<class K, class T, class KeyOfT, class HashFunc>
  101. class HashTable
  102. {
  103. template<class K, class T, class KeyOfT, class HashFunc>
  104. friend class __HTIterator;
  105. typedef HashNode<T> Node;
  106. public:
  107. typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
  108. iterator begin()
  109. {
  110. for (size_t i = 0; i < _tables.size(); ++i)
  111. {
  112. Node* cur = _tables[i];
  113. if (cur)
  114. {
  115. return iterator(cur, this);
  116. }
  117. }
  118. return end();
  119. }
  120. iterator end()
  121. {
  122. return iterator(nullptr, this);
  123. }
  124. ~HashTable()
  125. {
  126. for (size_t i = 0; i < _tables.size(); ++i)
  127. {
  128. Node* cur = _tables[i];
  129. while (cur)
  130. {
  131. Node* next = cur->_next;
  132. delete cur;
  133. cur = next;
  134. }
  135. _tables[i] = nullptr;
  136. }
  137. }
  138. size_t GetNextPrime(size_t prime)
  139. {
  140. const int PRIMECOUNT = 28;
  141. static const size_t primeList[PRIMECOUNT] =
  142. {
  143. 53ul, 97ul, 193ul, 389ul, 769ul,
  144. 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
  145. 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
  146. 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
  147. 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
  148. 1610612741ul, 3221225473ul, 4294967291ul
  149. };
  150. // 获取比prime大那一个素数
  151. size_t i = 0;
  152. for (; i < PRIMECOUNT; ++i)
  153. {
  154. if (primeList[i] > prime)
  155. return primeList[i];
  156. }
  157. return primeList[i];
  158. }
  159. pair<iterator, bool> Insert(const T& data)
  160. {
  161. HashFunc hf;
  162. KeyOfT kot;
  163. iterator pos = Find(kot(data));
  164. if (pos != end())
  165. {
  166. return make_pair(pos, false);
  167. }
  168. // 负载因子 == 1 扩容
  169. if (_tables.size() == _n)
  170. {
  171. //size_t newSize = _tables.size() == 0 ? 11 : _tables.size() * 2;
  172. size_t newSize = GetNextPrime(_tables.size());
  173. if (newSize != _tables.size())
  174. {
  175. vector<Node*> newTable;
  176. newTable.resize(newSize, nullptr);
  177. for (size_t i = 0; i < _tables.size(); ++i)
  178. {
  179. Node* cur = _tables[i];
  180. while (cur)
  181. {
  182. Node* next = cur->_next;
  183. size_t hashi = hf(kot(cur->_data)) % newSize;
  184. cur->_next = newTable[hashi];
  185. newTable[hashi] = cur;
  186. cur = next;
  187. }
  188. _tables[i] = nullptr;
  189. }
  190. newTable.swap(_tables);
  191. }
  192. }
  193. size_t hashi = hf(kot(data));
  194. hashi %= _tables.size();
  195. // 头插到对应的桶即可
  196. Node* newnode = new Node(data);
  197. newnode->_next = _tables[hashi];
  198. _tables[hashi] = newnode;
  199. ++_n;
  200. return make_pair(iterator(newnode, this), false);;
  201. }
  202. iterator Find(const K& key)
  203. {
  204. if (_tables.size() == 0)
  205. {
  206. return iterator(nullptr, this);
  207. }
  208. KeyOfT kot;
  209. HashFunc hf;
  210. size_t hashi = hf(key);
  211. //size_t hashi = HashFunc()(key);
  212. hashi %= _tables.size();
  213. Node* cur = _tables[hashi];
  214. while (cur)
  215. {
  216. if (kot(cur->_data) == key)
  217. {
  218. return iterator(cur, this);
  219. }
  220. cur = cur->_next;
  221. }
  222. return iterator(nullptr, this);
  223. }
  224. bool Erase(const K& key)
  225. {
  226. if (_tables.size() == 0)
  227. {
  228. return false;
  229. }
  230. HashFunc hf;
  231. KeyOfT kot;
  232. size_t hashi = hf(key);
  233. hashi %= _tables.size();
  234. Node* prev = nullptr;
  235. Node* cur = _tables[hashi];
  236. while (cur)
  237. {
  238. if (kot(cur->_data) == key)
  239. {
  240. if (prev == nullptr)
  241. {
  242. _tables[hashi] = cur->_next;
  243. }
  244. else
  245. {
  246. prev->_next = cur->_next;
  247. }
  248. delete cur;
  249. return true;
  250. }
  251. prev = cur;
  252. cur = cur->_next;
  253. }
  254. return false;
  255. }
  256. private:
  257. // 指针数组
  258. vector<Node*> _tables;
  259. size_t _n = 0;
  260. };
  261. }

Test.cpp

  1. #include<iostream>
  2. #include <set>
  3. #include <map>
  4. #include <unordered_set>
  5. #include <unordered_map>
  6. #include <time.h>
  7. using namespace std;
  8. #include "HashTable.h"
  9. #include "UnorderedMap.h"
  10. #include "UnorderedSet.h"
  11. void test_set()
  12. {
  13. unordered_set<int> s;
  14. //set<int> s;
  15. s.insert(2);
  16. s.insert(3);
  17. s.insert(1);
  18. s.insert(2);
  19. s.insert(5);
  20. //unordered_set<int>::iterator it = s.begin();
  21. auto it = s.begin();
  22. while (it != s.end())
  23. {
  24. cout << *it << " ";
  25. ++it;
  26. }
  27. cout << endl;
  28. for (auto e : s)
  29. {
  30. cout << e << " ";
  31. }
  32. cout << endl;
  33. }
  34. void test_op()
  35. {
  36. int n = 10000000;
  37. vector<int> v;
  38. v.reserve(n);
  39. srand(time(0));
  40. for (int i = 0; i < n; ++i)
  41. {
  42. //v.push_back(i);
  43. //v.push_back(rand()+i); // 重复少
  44. v.push_back(rand()); // 重复多
  45. }
  46. size_t begin1 = clock();
  47. set<int> s;
  48. for (auto e : v)
  49. {
  50. s.insert(e);
  51. }
  52. size_t end1 = clock();
  53. size_t begin2 = clock();
  54. unordered_set<int> us;
  55. for (auto e : v)
  56. {
  57. us.insert(e);
  58. }
  59. size_t end2 = clock();
  60. cout << s.size() << endl;
  61. cout << "set insert:" << end1 - begin1 << endl;
  62. cout << "unordered_set insert:" << end2 - begin2 << endl;
  63. size_t begin3 = clock();
  64. for (auto e : v)
  65. {
  66. s.find(e);
  67. }
  68. size_t end3 = clock();
  69. size_t begin4 = clock();
  70. for (auto e : v)
  71. {
  72. us.find(e);
  73. }
  74. size_t end4 = clock();
  75. cout << "set find:" << end3 - begin3 << endl;
  76. cout << "unordered_set find:" << end4 - begin4 << endl;
  77. size_t begin5 = clock();
  78. for (auto e : v)
  79. {
  80. s.erase(e);
  81. }
  82. size_t end5 = clock();
  83. size_t begin6 = clock();
  84. for (auto e : v)
  85. {
  86. us.erase(e);
  87. }
  88. size_t end6 = clock();
  89. cout << "set erase:" << end5 - begin5 << endl;
  90. cout << "unordered_set erase:" << end6 - begin6 << endl;
  91. }
  92. void test_map()
  93. {
  94. unordered_map<string, string> dict;
  95. dict.insert(make_pair("sort", "排序"));
  96. dict.insert(make_pair("left", "左边"));
  97. dict.insert(make_pair("left", "剩余"));
  98. dict["string"];
  99. dict["left"] = "剩余";
  100. dict["string"] = "字符串";
  101. }
  102. int main()
  103. {
  104. bit::test_set();
  105. //test_op();
  106. bit::test_map();
  107. //Bucket::TestHT3();
  108. //TestHT2();
  109. return 0;
  110. }

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

闽ICP备14008679号