【STL库】unordered_map/unordered_set 类学习
👓️博主简介:
文章目录
- 前言
- 一、unordered_set 系列的使用
- 1.1、unordered_set 和 unordered_multiset 参考文档
- 1.3、类函数学习
- 二、unordered_set 和 set 的使用差异
- 三、unordered_map 系列的使用
- 3.1、unordered_map 和 unordered_multimap 参考文档
- 3.2、unordered_map 类的介绍
- 3.3、类函数学习
- 四、unordered_map 和 map 的使用差异
- 五、unordered_multimap/unordered_multiset
- 总结
前言
我们在学习完 map/set 的底层原理和实现时,大家会不会有一种疑惑,map/set 的底层红黑树已经这么麻烦了,查找效率也只能达到 O(logN),那有没有查找效率是 O(1) 的容器呢,查找效率是 O(1) 的容器它的结构会不会比红黑树还要复杂的多呢?当然是有的,这就是我们的哈希表,也就是我们 unordered_map/unordered_set 容器。它的实现不会有大家想象的那么复杂,所以大家放心。当然,按流程来说我们先来了解了解它的接口吧。
一、unordered_set 系列的使用
1.1、unordered_set 和 unordered_multiset 参考文档
我们大家可以打开参考文档自行查看:<unordered_set> - C++ Reference
## 1.2、unordered_set 类的介绍 >我们来看看 unordered_set 类的声明,来分辨分辨它和 set 有什么区别。
- Key 就是 unordered_set 底层关键字的类型
- unordered_set 默认要求 Key 支持转换为整形,如果不支持或者想按自己的需求走可以自行实现支持将 Key 转成整形的仿函数传给第二个模板参数
- unordered_set 默认要求 Key 支持比较相等,如果不支持或者想按自己的需求走可以自行实现支持将 Key 比较相等的仿函数传给第三个模板参数
- unordered_set 底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第四个参数
- ⼀般情况下,我们都不需要传后三个模板参数
- unordered_set 底层是用哈希桶实现,增删查平均效率是 O(1),迭代器遍历不再有序,为了跟 set 区分,所以取名unordered_set
- 前面部分我们已经学习了 set 容器的使用,set 和 unordered_set 的功能高度相似,只是底层结构不同,有⼀些性能和使用的差异,这⾥我们只讲他们的差异部分。
1.3、类函数学习
我们 unordered_set 和 set 基本上是一样的,所以我们主要就只讲讲不同的地方,其他的就一笔带过了
(1)构造函数
默认构造:
unordered_set<int> us1;
迭代器区间构造:
vector<int> v({ 1, 2, 3, 4, 5, 6 });
unordered_set<int> us2(v.begin(), v.end());
拷贝构造: 
unordered_set<int> us3(us2);
列表构造: 
unordered_set<int> us4({ 1, 2, 3, 4, 5 , 6, 7, 8 });
(2)迭代器
unordered_set 迭代器不支持反向遍历,只能正向遍历。
unordered_set<int> us1({ 1, 2, 3, 4, 5 , 6, 7, 8 });unordered_set<int>::iterator it = us1.begin();
while (it != us1.end())
{cout << *it << " ";++it;
}
cout << endl;
当然,它的输出是随机输出的,而非按顺序输出。
unordered_set<int> us1({ 3, 4, 1, 0, 9, 2, 5, 6 });
它也具有去重的功能。
unordered_set<int> us1({ 3, 4, 1, 0, 9, 2, 5, 6, 1, 5, 3, 1 ,5 });
(3)增删查
增:
直接插入数值
unordered_set<int> us1;
us1.insert(1);
us1.insert(2);
us1.insert(3);
us1.insert(4);
插入一个迭代器区间
unordered_set<int> us1;
vector<int> v({ 2, 3, 6, 1, 8, 7 });
us1.insert(v.begin(), v.end());
插入一个列表
unordered_set<int> us1;
us1.insert({ 1, 4, 2, 8, 5 });
删:
和 set 完全一样,都是删除一个迭代器位置、删除一个值和删除一个迭代器区间,这里就不过多赘述。
查:
和 set 相同,找到了就返回那个位置的迭代器,否则就返回 end()。
unordered_set<int> us1({ 3, 4, 1, 0, 9, 2, 5, 6 });auto u1 = us1.find(5);
cout << *u1 << endl;auto u2 = us1.find(100);
cout << *(--u2) << endl;
二、unordered_set 和 set 的使用差异
查看文档我们会发现 unordered_set 的支持增删查和 set 的使用一模一样,关于使用我们这里就不再继续赘述和演示了。
- unordered_set 和 set 的第一个差异是对 key 的要求不同,set 要求 Key 支持小于比较,而 unordered_set 要求 Key 支持转成整形且支持等于比较,要理解 unordered_set 的这个两点要求得后续我们结合哈希表底层实现才能真正理解,也就是说这本质是哈希表的要求。
- unordered_set 和 set 的第二个差异是迭代器的差异,set 的 iterator 是双向迭代器,unordered_set 是单向迭代器,其次 set 底层是红黑树,红黑树是⼆叉搜索树,⾛中序遍历是有序的,所以 set 迭代器遍历是有序 + 去重。而 unordered_set 底层是哈希表,迭代器遍历是无序 + 去重。
- unordered_set 和 set 的第三个差异是性能的差异,整体而言大多数场景下,unordered_set 的增删查改更快一些,因为红黑树增删查改效率是 O(logN),而哈希表增删查平均效率是 O(1) 。
三、unordered_map 系列的使用
3.1、unordered_map 和 unordered_multimap 参考文档
我们大家可以打开参考文档自行查看:<unordered_map> - C++ Reference
3.2、unordered_map 类的介绍
- Key 就是 unordered_map 底层关键字的类型
- T 就是 unordered_map 与 Key 所对应的值的类型
- unordered_map 默认要求 Key 支持转换为整形,如果不支持或者想按自己的需求走可以自行实现支持将 Key 转成整形的仿函数传给第二个模板参数
- unordered_map 默认要求 Key 支持比较相等,如果不支持或者想按自己的需求走可以自行实现支持将 Key 比较相等的仿函数传给第三个模板参数
- unordered_map 底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第四个参数
- ⼀般情况下,我们都不需要传后三个模板参数
- unordered_map 底层是用哈希桶实现,增删查平均效率是 O(1),迭代器遍历不再有序,为了跟 map 区分,所以取名unordered_map
- 前面部分我们已经学习了 map 容器的使用,map 和 unordered_map 的功能高度相似,只是底层结构不同,有⼀些性能和使用的差异,这⾥我们只讲他们的差异部分。
3.3、类函数学习
我们 unordered_map 和 map 基本上是一样的,所以我们主要就只讲讲不同的地方,其他的就一笔带过了
(1)构造函数
默认构造:
unordered_map<int, int> um1;
迭代器区间构造:
vector<pair<int, int>> v({ {1, 2}, {2, 2}, {3, 2}, {4, 2} });
unordered_map<int, int> um2(v.begin(), v.end());
拷贝构造:
unordered_map<int, int> um3(um2);
列表构造: 
unordered_map<int, int> um4({ {1, 2}, {2, 2}, {3, 2}, {4, 2} });
(2)迭代器
unordered_map 迭代器不支持反向遍历,只能正向遍历。
unordered_map<int, int> um1({ {3, 2}, {4, 2}, {1, 2}, {9, 2}, {5, 2} });
unordered_map<int, int>::iterator it = um1.begin();
while (it != um1.end())
{cout << it->first << " " << it->second << endl;it++;
}
cout << endl;
他的内容也是随机输出的,而非按顺序输出。unordered_map 具有去重的功能。
unordered_map<int, int> um1({ {3, 2}, {4, 2}, {1, 2}, {9, 2}, {5, 2}, {3, 7}, {1, 4} });
和 map 是一样的。
(3)增删查
和 map 是一样的,这里也就不多说了,大家可以自己去看看参考文档。
四、unordered_map 和 map 的使用差异
查看文档我们会发现 unordered_map 的支持增删查改和 map 的使用一模一样,关于使用我们这里也就不再继续赘述和演示了。
- unordered_map 和 map 的第一个差异是对 key 的要求不同,map 要求 Key 支持小于比较,而 unordered_map 要求 Key 支持转成整形且支持等于比较,要理解 unordered_map 的这个两点要求得后续我们结合哈希表底层实现才能真正理解,也就是说这本质是哈希表的要求。
- unordered_map 和 map 的第二个差异是迭代器的差异,map 的 iterator 是双向迭代器,unordered_map 是单向迭代器,其次map 底层是红黑树,红黑树是二叉搜索树,⾛中序遍历是有序的,所以 map 迭代器遍历是 Key 有序 + 去重。而 unordered_map 底层是哈希表,迭代器遍历是 Key 无序 + 去重。
- unordered_map 和 map 的第三个差异是性能的差异,整体而言大多数场景下,unordered_map 的增删查改更快⼀些,因为红黑树增删查改效率是 O(logN),而哈希表增删查平均效率是 O(1)。
五、unordered_multimap/unordered_multiset
- unordered_multimap/unordered_multiset 跟 multimap/multiset 功能完全类似,支持 Key 冗余。
- unordered_multimap/unordered_multiset 跟 multimap/multiset 的差异也是三个方面的差异,key 的要求的差异,iterator 及遍历顺序的差异,性能的差异。
总结
以上便是我们 unordered_map/unordered_set 类函数的全部学习内容,因为我们前面已经学习过非常多的容器了,而且这里也和我们前面 map/set 上层几乎是一样的,只是效率的区别,所以我们赶快讲完它类函数的使用,去看看它是怎么实现我们 O(1) 效率的查找的吧,我们下一章再见。
🎇坚持到这里已经很厉害啦,辛苦啦🎇 ʕ • ᴥ • ʔ づ♡ど