我正在尝试创建一个unordered_map
来映射带有整数的对:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
我有一个班级,我已将Unordered_map
声明为私人会员。
但是,当我尝试编译它时,我收到以下错误:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c ++ / v1 / type_traits:948:38:未定义模板的隐式实例化&#st; :: __ 1 :: hash ,std :: __ 1 :: basic_string&gt; &GT;&#39;
如果我使用map<pair<string, string>, int>
之类的常规地图而不是unordered_map
,我就不会收到此错误。
是否无法在无序地图中使用pair
作为关键字?
答案 0 :(得分:9)
我解决此问题的首选方法是定义一个key
函数,将您的对转换为唯一的整数(或任何可哈希的数据类型)。此密钥不是哈希密钥。它是数据对的唯一ID,然后由unordered_map
进行最佳散列。例如,您想要定义类型
unordered_map
unordered_map<pair<int,int>,double> Map;
您希望使用Map[make_pair(i,j)]=value
或Map.find(make_pair(i,j))
来操作地图。然后你必须告诉系统如何散列一对整数make_pair(i,j)
。而不是那样,我们可以定义
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
然后将地图类型更改为
unordered_map<size_t,double> Map;
我们现在可以使用Map[key(i,j)]=value
或Map.find(key(i,j))
来操作地图。现在每个make_pair
都会调用内联key
函数。
这种方法可以保证密钥的最佳散列,因为现在散列部分由系统完成,系统总是选择内部散列表大小来确保每个桶都具有相同的可能性。但是你必须让自己100%确定key
对于每一对都是唯一的,即,没有两个不同的对可以拥有相同的密钥,或者可能存在非常难以找到的错误。
答案 1 :(得分:7)
对于配对密钥,我们可以使用增强对散列函数:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
类似地,我们可以对向量使用boost hash,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
答案 2 :(得分:4)
参考:C++ Standard Library: A tutorial and reference, Second version第7.9.2章:创建和控制无序容器
我在Google中找到的所有解决方案都使用XOR
生成pair
的哈希码,这是非常糟糕的。参见why-is-xor-the-default-way-to-combine-hashes。但是,本书使用hash_combine
(从Boost
中提取)为我们提供了最佳解决方案。当我在Online Judge(Atcoder)中对其进行测试时,该解决方案比XOR更好。我将代码组织为模板,如下所示。您可以复制和粘贴它。更改它以适合任何自定义结构/类很方便。
更新:为元组添加哈希模板。
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
答案 3 :(得分:0)
与其他答案相比,我知道这是过于幼稚的实现,但是有一种解决方法。
如果要获取对的输入,只需在获取输入时用unordered_hash中的另一个整数对对进行哈希处理,然后间接使用此整数值对对进行哈希处理,即
unordered_hash<int, Vote> h;
using Unordered_map = unordered_map<i, int>; // i is corresponding value to pair
答案 4 :(得分:0)
在 Baum mit Augen 对answer的评论中,用户 Joe Black asked for an example改用lambda expressions哈希函数的定义。我同意 Baum mit Augen 的opinion,认为这可能会损害可读性,特别是如果您想实现更通用的解决方案。因此,我想通过OP提出的std::pair<std::string, std::string>
特定解决方案来简化我的示例。该示例还使用了std::hash<std::string>
函数调用的handcrafted组合:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
答案 5 :(得分:0)
如果不是严格要求pair
,则只需两次使用map。
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
答案 6 :(得分:0)
此类问题有hack
使用 std:unordered_map
的 string
看下面的例子-
我需要散列矩形的端点(角)
<块引用>错误处理
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
<块引用>
破解
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
如果您需要创建十进制或双精度的哈希作为键,这种 hack 甚至可以工作:)