unordered_map需要头文件#include <unordered_map>
unordered_set需要头文件#include <unordered_set>
二者都是基于哈希实现的,无序,因此增删改查的时间复杂度是 O(1),比map和set快,它们是基于平衡二叉树(红黑树)实现的,动态维护有序序列,时间复杂度 O(logn)。
关于如何手写哈希表请参考:哈希表、字符串哈希初步学习
unordered_map介绍
unordered_map<key,value> m;
定义一个map类型,key是主键,value是值,可以实现从主键到值的映射,是二维数组的升级,二维数组中,主键只能是int类型,下标从0开始,map可以的主键可以是任何类型。
基本框架如下:
#include <iostream>
#include <unordered_map>
using namespace std;
typedef pair<string,int> PSI;
int main(){
unordered_map<string,int> hash;
return 0;
}
常用操作
hash.size()
返回当前map 容器大小(元素个数)
hash.empty()
判断 map 容器是否为空,空返回1,非空返回0
hash.clear()
清空 map 中所有元素
hash.erase(key)
通过 key 删除元素
hash.erase(it)
删除迭代器 it 指向的元素 可与find函数结合起来使用
1. 插入元素
① hash["lyh"]=20;
插入过程,先取出hash[key],再重新赋值。
如果就写hash[key]
,key存在的话,就只有取出过程;如果key不存在的话,就是插入过程,默认插入0。
② hash.insert(PSI("lyh",40));
等价于:PSI t={"efg",456}; hash.insert(t);
了解pair二元结构的用法,也可以可以单独赋值,
t.first="efg";
t.second=456;
①和②都可以插入,不过①可以实现更新,由于map中主键key只能有一个,不能重复,当key已经存在于map中时,使用方法①可以进行更新,而使用方法②就不会插入了。
2. 访问元素
① 通过主键单个访问
cout<<hash["lyh"];
②通过迭代器可以实现全部遍历或者单个访问
for (auto it=hash.begin();it!=hash.end();it++)
cout<<it->first<<" "<<it->second<<endl;
auto是C++中的写法,可以自动判断变量的类型,实际上应该是
unordered_map<string,int>::iterator it;
③stl容器特有的for循环
for (auto t:hash)
cout<<t.first<<" "<<t.second;
auto
即为pair<int,int>
类型。
对方式①的访问需要注意,
hash[key]
就是一次查找操作,返回当前key的value值,如果这个key在hash中不存在时,就会默认插入,初值为0,因此在不确定key是否存在的情况下,最好通过find或者count函数确定key是否存在,避免误插入hash中。
3. 判断 主键 是否存在
hash.find(key);
查找指定 key 是否存在,存在返回指向该 key 的迭代器,否则返回指向 hash.end() 的迭代器
hash.count(key);
返回 key 对应的元素个数,由于 map 容器 key 不允许重复,因此返回值只能为 0 或 1,即也是判断键值元素是否存在。
unordered_set介绍
unordered_set<int> hash;
上面说到的map中映射是key->value,key和value都可以是任何类型,但是主键key不能重复;
而set就更像是数组了,只能是int->value的映射,即主键只能是int类型,类似于vector容器,不过set的value值不允许重复。而且也不支持根据根据 主键int 进行索引,只能通过迭代器遍历,即set只能存储value值。
基本框架如下:
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> hash;
return 0;
}
常用操作
hash.size();
获取元素个数
hash.empty();
判断是否为空
hash.clear();
清空set
hash.erase(x);
删除值为x的元素
hash.erase(it);
删除迭代器it指向的元素
hash.erase(first,last);
删除迭代器指向的 [first,last) 之间的元素
1. 插入元素 insert函数
hash.insert(x);
将元素x插入到集合中
2. 访问元素
vector支持下标访问,map支持主键访问。
set既不支持主键也不支持下标。
for (auto it=hash.begin();it!=hash.end();it++)
cout<<(*it)<<endl;
同理,auto即代表unordered_set<int>::iterator it;
for (auto t:hash) cout<<t<<endl;
3. 判断元素是否存在
map中不能判断value是否存在,是判断主键key是否存在的,而且主键不能重复; set中由于主键确定,set是判断value值是否存在的。
hash.find(x);
返回一个迭代器,指向元素x,如果不存在则指向hash.end();
hash.count(x);
判断集合中是否存在该元素x,返回值为1或者0。
哈希表中常用的操作就是插入和取数,判断一个数是否存在,很少几乎不使用删除,可以适当选择set或者map模拟。
map与set的区别在于 :
map中存的是一对值<类型1,类型2> (<key,value>),而set中存的是一个值 。
具体在find函数和count函数区别也挺大,map是找主键,set是找值。
题目描述
在蓝字题目描述的基础上,介绍如何用unordered_map、unordered_set代替。
简要题目描述:
维护一个集合,支持如下几种操作:
“I x”,插入一个数x;
“Q x”,询问数x是否在集合中出现过;
现在要进行N次操作,对于每个询问操作输出对应的结果。
数据范围
1≤N≤105
−109≤x≤109
分析:
取一个什么样的集合呢?输入数据的x范围取到了2e9的范围,从-1e9到1e9,但是实际输入的数据只有1e5个,如果像桶排那样开一个巨大的数组来存储,很明显会造成浪费,因此采用离散化的思想,将输入的[-1e9,1e9]的数据进行映射到[1,1e5]数据之间,所以采用哈希,之前是手写哈希,现在用stl容器,但是手写哈希优势比较大,速度比较快。
使用unordered_set模拟
set只能存value值,只能根据value值进行查找,所以要把每次的x都当作value值插入,幸好题目要求的是x是否出现过,而不是求x出现的次数,因为set容器不能存储重复元素。
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
unordered_set<int> hash;
int num,x;
char op[2];
cin>>num;
while (num--) {
cin>>op>>x;
if (op[0]=='I') hash.insert(x);
else hash.count(x)?puts("Yes"):puts("No");
}
return 0;
}
使用unordered_map模拟
unordered_map <key,value> ,unordered_map插入的是一个二元结构体<key,value>,由key映射到value,且只能根据key找,不能根据value找。所以把每次输入的x作为主键key,value值维护主键key出现的的次数,由于题目没有要求次数,所以直接存储1标志出现过即可。
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
unordered_map <int,int> hash;
int num,x;
char op[2];
cin>>num;
while (num--) {
cin>>op>>x;
if (op[0]=='I') hash[x]=1; //根据key在那里标记是否插入过
else hash.count(x)?puts("Yes"):puts("No");
} "hash.find(x)找到的是迭代器,找不到即指向hash.end()"
return 0;
}