unordered_set与unordered_map的声明为
template<class Key,class Hash=hash<Key>,class Pred=equal_to<Key>,class Alloc=allocate<Key>>
class unordered_set;
template<class Key,class T,class Hash=hash<Key>,class Pred=equal_to<Key>,class Alloc=allocate<pair<const Key,T>>>
class unordered_map;
通常指定第一个模版参数与第二个模版参数即可使用,无需显示指定模版参数Hash
、Pred
与Alloc(空间配置器)
unordered_set<int> hashset;
unordered_map<string,string> hashmap;
因为在std下有针对int类型和string类型特化的hash
与hash
结构,在该结构中由系统提供了将int类型和string类型转化为哈希值的仿函数。
如果哈希表中存放的key值类型在std::hash中没有完成特化,那么在使用unordered_set与unordered_map时会出现错误,例如
unordered_set<array<int, 26>> hashset;//error
此时就需要自定义哈希函数与比较器,因为系统不知道如何将类似array
特化std::hash与std::equal_to
将std::hash和std::equal_to针对特定类型进行特化可以避免上述错误
namespace std{
template<>
struct hash<array<int,26>>{
size_t operator()(const array<int,26>& arr)const{
size_t hashValue=0;
for(int i=0;i<26;i++){
hashValue=hashValue*31+std::hash<int>()(arr[i]);
}
return hashValue;
}
};
template<>
struct equal_to<array<int,26>>{
bool operator()(const array<int,26>& arr1,const array<int,26>& arr2)const{
for(int i=0;i<26;i++){
if(arr1[i]!=arr2[i]){
return false;
}
}
return true;
}
};
}
unordered_set<array<int, 26>> hashset;//success
在对std::hash和std::equal_to进行特化时,其结构内部的仿函数应该使用const进行修饰,否则在使用时可能发生错误。
除此之外,还可以使用下面的方式进行特化,起到的效果一致
namespace std{
template<typename T,size_t N>
struct hash<array<T,N>>{
size_t operator()(const array<T,N>& arr)const{
size_t hashValue=0;
for(int i=0;i<N;i++){
hashValue=hashValue*31+std::hash<T>()(arr[i]);
}
return hashValue;
}
};
template<typename T,size_t N>
struct equal_to<array<T,N>>{
bool operator()(const array<T,N>& arr1,const array<T,N>& arr2)const{
for(int i=0;i<N;i++){
if(arr1[i]!=arr2[i]){
return false;
}
}
return true;
}
};
}
自定义比较结构
自定义比较结构时,需要显示指出模版参数的类型
struct ArrayHash {
size_t operator()(const array<int, 26>& arr)const {
size_t hashValue = 0;
for (int i = 0; i < 26; i++) {
hashValue = hashValue * 31 + std::hash<int>()(arr[i]);
}
return hashValue;
}
};
struct ArrayPred {
bool operator()(const array<int, 26>& arr1, const array<int, 26>& arr2)const {
for (int i = 0; i < 26; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
};
//第二个模版参数为ArrayHash,第三个模版参数为ArrayPred
unordered_set<array<int,26>,ArrayHash,ArrayPred> hashset;
使用lambda表达式
可以使用lambda表达式配合decltype完成自定义哈希函数与比较器
auto ArrayHash = [](const array<int, 26>& arr) {
size_t hashValue = 0;
for (int i = 0; i < 26; i++) {
hashValue = hashValue * 31 + std::hash<int>()(arr[i]);
}
return hashValue;
};
auto ArrayPred = [](const array<int, 26>& arr1, const array<int, 26>& arr2) {
for (int i = 0; i < 26; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
};
由于lambda表达式闭包的特点,其对应的类型无法生成默认对象(例如上述decltype(ArrayPred) obj
是错误的),因此需要在构造函数中将lambda对象传入,unordered_set存在如下构造函数:
explicit unordered_set(size_t n=/*依据平台与库的实现*/,const hasher& hf=hasher(),const key_equal& eql=key_equal(),const allocator_type& alloc=allocator_type());
其中n为初始时哈希表的大小,与平台和库的实现有关,由于lambda闭包,第二个参数与第三个参数必须传入指定lambda对象,这就需要我们对第一个参数进行设置,因此,使用lambda表达式完成自定义哈希函数与比较器,在进行实例化对象时,需要采取如下写法
unordered_set<array<int,26>,decltype(ArrayHash),decltype(ArrayPred)> hashset(20,ArrayHash,ArrayPred);
第一个参数为哈希表的初始大小(必须指定)。
map与set的声明
template<class Key,class T,class Compare=less<Key>,class Alloc=allocator<pair<const Ket,T>>>
class map;
template<class Key,class Compare=less<Key>,class Alloc=allocator<T>>
class set;
在定义对象时,需要保证模版参数对应的类型可以进行比较操作,若不能,则需要自定义比较操作。
namespace std {
template<class T,size_t N>
struct less<array<T,N>> {
bool operator()(const array<T, N>& left, const array<T, N>& right)const {
return left[0] < right[0];
}
};
}
set<array<int, 26>> s;
自定义比较操作可以针对std::less进行特化,可以自定义比较结构,也可以使用lambda表达式,若使用lambda表达式,需要在构造函数中传入lambda对象
struct ArrayComp {
bool operator()(const array<int, 26>& left, const array<int, 26>& right)const {
return left[0] < right[0];
}
};
set<array<int, 26>,ArrayComp> s;
lambda:
auto ArrayComp = [](const array<int, 26>& left, const array<int, 26>& right) {
return left[0] < right[0];
};
set<array<int, 26>, decltype(ArrayComp)> s(ArrayComp);
leetcode字母异位词分组
在此题中,可以使用哈希表,并将key设置为array
namespace std{
template<typename T,size_t N>
struct hash<array<T,N>>{
size_t operator()(const array<T,N>& arr)const{
size_t hashValue=0;
for(const auto& i:arr){
hashValue=hashValue*31+std::hash<T>()(i);
}
return hashValue;
}
};
template<typename T,size_t N>
struct equal_to<array<T,N>>{
size_t operator()(const array<T,N>& arr1,const array<T,N>& arr2)const{
for(int i=0;i<26;i++){
if(arr1[i]!=arr2[i]){
return false;
}
}
return true;
}
};
}
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
unordered_map<array<int,26>,vector<string>> hashmap;
for(string& s:strs){
array<int,26> charcnts{};
for(char c:s){
charcnts[c-'a']++;
}
hashmap[charcnts].push_back(move(s));
}
for(auto&[k,vecstr]:hashmap){
ans.push_back(move(vecstr));
}
return ans;
}
};