unordered_map、unordered_set代替手写哈希

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;

对方式①的访问需要注意,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;

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;
}

猜你喜欢

转载自blog.csdn.net/HangHug_L/article/details/114135343