写题的时候经常要判断一个区间或者一条边/点是否存在。会写出这种代码
unordered_map<pair<int, int>, int>m1;
unordered_map<node, int>m2;
很明显报错了。理由是STL unordered_map/set只对基本数据类型和string做了hash。也就是不仅是自定义类型,pair这种的hash也要自己写。
首先来分析unordered_map/set的参数
unordered_set<typename _Kty,typename _Hasher=hash<_Kty>,typename _Keyeq=equal_to<_Kty>,typename _Allocator<_Kty>>
unordered_map<typename _Kty,typename _Value,typename _Hasher=hash<_Kty>,typename _Keyeq=equal_to<_Kty>,typename _Allocator<_Kty>>
以unordered_map为例,
前两个参数为(key,value)键值对。
第三个参数即为hash标准,
而第四个参数为判断元素相同的函数 因为map和set需要判断新加入元素和容器内元素的键值是否相同来安排元素的位置。
也就是说,我们需要一个仿函数实现该复杂类型的hash,同时还需要给这个类型重载==(equal)以至于map/set可以安排他的位置。
得到hash值可以通过STL自带的hash模板函数,但是只能对基本数据类型和string做hash。不过我们可以分解复杂类型内的基本类型,用复杂类型的基本类型组合成新的hash值代表这个复杂类型。
hash<typename>()(valua)
补充:所谓仿函数也就是重载在一个结构体/类中的运算符函数。
struct node {
int x, y;
int operator==(const node& a)const {//自定义类型要加==重载
if(x==a.x&&y==a.y)
return 1;
}
};
struct hashf {
template<class T,class U>
int operator()(const pair<T, U>& a)const {
return hash<int>()(a.first)*13 ^ hash<int>()(a.second)*31;
}
int operator()(const node& a)const {
return hash<int>()(a.x)*13 ^ hash<int>()(a.y)*31;//基本类型组合成新的hash
}
};
unordered_map<pair<int, int>, int,hashf>m1;
unordered_map<node, int, hashf>m2;
这样就可以实现,对pair和自定义类型的hash了
注意,如果hash函数太弱的的话会频繁产生hash冲突,复杂度会大幅提高。
但是map和set不需要这么麻烦,map可以直接存入pair,是因为,map和set只需要重载比较运算符进行排序,而unordered_map底层是hash表,所以需要hash值。