一、unordered_set的使⽤
1、参考文档
unordered_set参考文档
unordered_multiset参考文档
2、unordered_set类的介绍
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
unordered_set的声明如上,Key就是unordered_set底层关键字的类型。
unordered_set默认要求Key⽀持转换为整形,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key转成整形的仿函数传给第⼆个模板参数。
unordered_set默认要求Key⽀持⽐较相等,如果不⽀持或者想按⾃⼰的需求⾛可以⾃⾏实现⽀持将Key⽐较相等的仿函数传给第三个模板参数。
unordered_set底层存储数据的内存是从空间配置器申请的,如果需要可以⾃⼰实现内存池,传给第四个参数。
unordered_set底层是⽤哈希桶实现的,增删查平均效率是O(1) ,迭代器遍历不再有序,为了跟set区分,所以取名unordered_set。
3、unordered_set的常用接口
1)constructor
和set一样,unordered_set也支持⽆参默认构造、迭代器区间构造、拷贝构造、列表初始化以及赋值重载。
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
//默认去重+不排序
unordered_set<int> us;//默认无参构造
unordered_set<int> us1 = { 3,5,4,1,7,5,8,3,9,10 };//初始化列表
unordered_set<int> us2(us1.begin(), us1.end());//迭代器区间构造
unordered_set<int> us3(us1);//拷贝构造
unordered_set<int> us4;
us4 = us1;//赋值
return 0;
}
2)Iterators
unordered_set的底层是哈希桶,是单向链表,因此只支持单向迭代器。
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
unordered_set<int> us1 = { 3,5,4,1,7,5,8,3,9,10 };//初始化列表
unordered_set<int>::iterator it = us1.begin();
while (it != us1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
运行结果:
3)Element lookup
unordered_set还支持find接口。
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
unordered_set<int> us = { 5,6,1,2,4,5,3 };
auto it = us.find(3);//查找
cout << *it << endl;
return 0;
}
运行结果:
4)Modifiers
unordered_set还支持插入、删除接口。
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
unordered_set<int> us;
us.insert(10);//插入
us.insert(6);
us.insert(20);
us.insert(30);
us.insert(15);
us.erase(6);//删除
for (auto e : us)
{
cout << e << " ";
}
cout << endl;
return 0;
}
运行结果:
5)Buckets
这里是有关哈希桶的相关接口。
函数声明 | 接口说明 |
---|---|
bucket_count | 返回桶的数量 |
max_bucket_count | 返回桶的最大数量 |
bucket_size | 返回桶的大小 |
bucket | 定位元素所在的桶 |
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
unordered_set<int> us = { 5,6,1,2,4,5,3,9 };
cout << us.bucket_count() << endl;//桶的数量
cout << us.max_bucket_count() << endl; //桶的最大数量
cout << us.bucket_size(0) << endl;//桶0的大小
cout << us.bucket(9) << endl;//9所在的桶
return 0;
}
运行结果:
6)Hash policy
函数声明 | 接口说明 |
---|---|
load_factor | 返回负载因子 |
max_load_factor | 获取或设置最大负载因子 |
rehash | 设置桶的数量 |
reserve | 更改容量,与rehash用法近似,都起到设置桶的数量的作用 |
#include<iostream>
#include<unordered_set>
using namespace std;
int main()
{
unordered_set<int> us = { 5,6,1,2,4,5,3,9 };
cout << us.load_factor() << endl;//负载因子
cout << us.max_load_factor() << endl;//最大的负载因子
cout << us.bucket_count() << endl;
us.rehash(20);//设置桶的数量
cout << us.bucket_count() << endl;
us.reserve(20);//更改容量
cout << us.bucket_count() << endl;
return 0;
}
运行结果:
4、unordered_set和set的差异
- unordered_set和set的第⼀个差异是对key的要求不同,set要求Key⽀持⼩于⽐较,⽽unordered_set要求Key⽀持转成整形且⽀持等于⽐较,这本质是哈希表的要求。
- unordered_set和set的第⼆个差异是迭代器的差异,set的iterator是双向迭代器,unordered_set是单向迭代器,其次set底层是红⿊树,红⿊树是⼆叉搜索树,⾛中序遍历是有序的,所以set迭代器遍历是有序+去重。⽽unordered_set底层是哈希表,迭代器遍历是⽆序+去重。
- unordered_set和set的第三个差异是性能的差异,整体⽽⾔⼤多数场景下,unordered_set的增删查改更快⼀些,因为红⿊树增删查改效率是O(logN) ,⽽哈希表增删查平均效率是O(1) ,具体可以参看下⾯代码的演⽰的对⽐差异。
#include<iostream>
#include<unordered_set>
#include<set>
#include<vector>
using namespace std;
int main()
{
const size_t N = 1000000;
unordered_set<int> us;
set<int> s;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
//v.push_back(rand());//重复多
v.push_back(rand() + i);//重复少
//v.push_back(i);//无重复 有序
}
//插入N个数据
size_t begin1 = clock();
for (auto e : v)
{
s.insert(e);
}
size_t end1 = clock();
cout << "set insert: " << end1 - begin1 << "ms" << endl;
size_t begin2 = clock();
us.reserve(N);
for (auto e : v)
{
us.insert(e);
}
size_t end2 = clock();
cout << "unordered_set insert: " << end2 - begin2 << "ms" << endl << endl;;
cout << "set插入数据个数: " << s.size() << endl;
cout << "unordered_set插入数据个数: " << us.size() << endl << endl;
//依次查找N个数据
int m1 = 0;
size_t begin3 = clock();
for (auto e : v)
{
auto ret = s.find(e);
if (ret != s.end())
{
++m1;
}
}
size_t end3 = clock();
cout << "set find:" << m1 << "->" << end3 - begin3 << "ms" << endl;
int m2 = 0;
size_t begin4 = clock();
for (auto e : v)
{
auto ret = us.find(e);
if (ret != us.end())
{
++m2;
}
}
size_t end4 = clock();
cout << "unordered_set find:" << m2 << "->" << end3 - begin3 << "ms" << endl << endl;
//删除N个数据
size_t begin5 = clock();
for (auto e : v)
{
s.erase(e);
}
size_t end5 = clock();
cout << "set erase: " << end5 - begin5 << "ms" << endl;
size_t begin6 = clock();
for (auto e : v)
{
s.erase(e);
}
size_t end6 = clock();
cout << "unordered_set erase: " << end6 - begin6 << "ms" << endl;
return 0;
}
运行结果:
从上面的结果中可以发现unordered_set的插入与删除效率是要更高的。
二、unordered_map的使用
1、参考文档
unordered_map参考⽂档
unordered_multimap参考文档
2、unordered_map类的介绍
template < class Key, // unordered_map::key_type
class T, // unordered_map::mapped_type
class Hash = hash<Key>, // unordered_map::hasher
class Pred = equal_to<Key>, // unordered_map::key_equal
class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type
> class unordered_map;
unordered_map类的定义与unordered_set类是一致的,只是多了一个模版参数T,用来表示映射值的数据类型。
3、unordered_map的常用接口
1)constructor
和map一样,unordered_map也支持⽆参默认构造、迭代器区间构造、拷贝构造、初始化列表以及赋值重载。
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um;//无参默认构造
unordered_map<string, string> um1 = { {"left","左边"},{"right","右边"},{"insert","插入"} };//初始化列表
unordered_map<string, string> um2(um1);//拷贝构造
unordered_map<string, string> um3;
um3 = um1;//赋值
return 0;
}
2)Iterators
unordered_map的底层是哈希桶,是单向链表,因此只支持单向迭代器。
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um1 = { {"left","左边"},{"right","右边"},{"insert","插入"} };
unordered_map<string, string>::iterator it = um1.begin();
while (it != um1.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
return 0;
}
运行结果:
3)Element access
相较于unordered_set,unordered_map还重载了[ ],可以通过[ ]实现插入、修改、查找等等作用。
函数声明 | 接口说明 |
---|---|
operator[ ] | 访问元素 |
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um1 = { {"left","左边"},{"right","右边"},{"insert","插入"} };
um1["front"] = "前面";//插入
um1["insert"] = "插入元素";//修改
cout << um1["front"] << endl;//访问
for (auto e : um1)
{
cout << e.first << ":" << e.second << endl;
}
return 0;
}
运行结果:
4)Element lookup
unordered_map还支持find接口。
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um = { {"left","左边"},{"right","右边"},{"insert","插入"} };
auto it = um.find("left");//查找
cout << it->first << ":" << it->second << endl;
return 0;
}
运行结果:
5)Modifiers
unordered_map还支持插入、删除接口。
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um = { {"left","左边"},{"right","右边"},{"insert","插入"} };
um.insert({ "back","后面" });//插入
um.erase("right");//删除
for (auto e : um)
{
cout << e.first << ":" << e.second << endl;
}
return 0;
}
运行结果:
6)Buckets
函数声明 | 接口说明 |
---|---|
bucket_count | 返回桶的数量 |
max_bucket_count | 返回桶的最大数量 |
bucket_size | 返回桶的大小 |
bucket | 定位元素所在的桶 |
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um = { {"left","左边"},{"right","右边"},{"insert","插入"} };
cout << um.bucket_count() << endl;//桶的数量
cout << um.max_bucket_count() << endl;//桶的最大数量
cout << um.bucket_size(0) << endl;//桶0的大小
cout << um.bucket("left") << endl;//"left"所在的桶
return 0;
}
运行结果:
7)Hash policy
函数声明 | 接口说明 |
---|---|
load_factor | 返回负载因子 |
max_load_factor | 获取或设置最大负载因子 |
rehash | 设置桶的数量 |
reserve | 更改容量,与rehash用法近似,都起到设置桶的数量的作用 |
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, string> um = { {"left","左边"},{"right","右边"},{"insert","插入"} };
cout << um.load_factor() << endl;//负载因子
cout << um.max_load_factor() << endl;//最大的负载因子
cout << um.bucket_count() << endl;
um.rehash(20);//设置桶的数量
cout << um.bucket_count() << endl;
um.reserve(20);//更改容量
cout << um.bucket_count() << endl;
return 0;
}
运行结果:
4、unordered_map和map的差异
- unordered_map和map的第⼀个差异是对key的要求不同,map要求Key⽀持⼩于⽐较,而unordered_map要求Key⽀持转成整形且⽀持等于⽐较,这本质是哈希表的要求。
- unordered_map和map的第⼆个差异是迭代器的差异,map的iterator是双向迭代器,unordered_map是单向迭代器,其次map底层是红⿊树,红⿊树是⼆叉搜索树,⾛中序遍历是有序的,所以map迭代器遍历是Key有序+去重。⽽unordered_map底层是哈希表,迭代器遍历是Key⽆序+去重。
- unordered_map和map的第三个差异是性能的差异,整体⽽⾔⼤多数场景下,unordered_map的增删查改更快⼀些,因为红⿊树增删查改效率是O(logN) ,⽽哈希表增删查平均效率是O(1)。
5、 unordered_multimap/unordered_multiset
- unordered_multimap/unordered_multiset跟multimap/multiset功能完全类似,⽀持Key冗余。
- unordered_multimap/unordered_multiset跟multimap/multiset的差异也是三个⽅⾯的差异,key的要求的差异,iterator及遍历顺序的差异,性能的差异。