使用无序密钥组合进行地图查找的C ++

时间:2016-07-05 10:50:19

标签: c++ unordered-map

我想创建一个unordered_map,其中键是两个整数的组合。由于在比较时忽略了键值顺序,我想到使用unordered_set作为这样的键:

#include <unordered_set>
#include <unordered_map>

using namespace std;

int main ()
{
    unordered_set<int> key_set1 = {21, 42};
    unordered_map<unordered_set<int>, char> map;
    map[key_set1] = 'a';
    ...
    unordered_set<int> key_set2 = {42, 21};
    if(map[key_set2] == map[key_set2])
        success();
}

在编译时,它看起来像哈希函数的一些问题:

error: no match for call to ‘(const std::hash<std::unordered_set<int> >) (const std::unordered_set<int>&)’
  noexcept(declval<const _Hash&>()(declval<const _Key&>()))>

我该如何解决这个问题?或者有更好的方式/数据结构吗?

4 个答案:

答案 0 :(得分:3)

问题是unordered_set不是为在无序容器中用作密钥而构建的。

如果你总是使用两个整数,那么使用一对整数作为键是更经济的,并添加一个函数,从两个整数形成一个正确排序的对:

pair<int,int> unordered_key(int a, int b) {
    return a<b?make_pair(a, b):make_pair(b, a);
}

答案 1 :(得分:3)

unordered_set没有预定义的哈希函数,所以你必须实现自己的哈希函数;这里有http://en.cppreference.com/w/cpp/utility/hash的文档。

基本上你需要:

// custom specialization of std::hash can be injected in namespace std
namespace std
{
    template<> struct hash<unordered_set<int>>
    {
        std::size_t operator()(unordered_set<int> const& s) const
        {
            std::size_t hash = 0;
            for (auto && i : s) hash ^= std::hash<int>()(i);
            return hash;
        }
    };
}

现在xor并不是推荐的组合哈希函数的方法,但它应该在这种情况下有效,因为它既有无序又有设置即可。因为它无序,你需要一个可交换的功能。建议的哈希合并器不具备您通常想要的属性&#34; abc&#34;哈希与&#34; bca&#34;不同。其次,它是一套确保你不会有任何重复元素的事实。这可以使您的哈希函数免于失败,因为x ^ x == 0

我还应该提一下,你想在cpp文件中定义它,这样你就不会在std类型上向每个人公开这个特定的哈希实现。

答案 2 :(得分:2)

如前所述,直接使用std::pair作为键,您需要为其明确定义哈希函数。如果你想避免这种情况,你可以将2个无符号整数按顺序组合成1:

uint64_t makeKey(uint32_t a, uint32_t b)
{
    return a < b ? (static_cast<uint64_t>(a) << 32) + b : (static_cast<uint64_t>(b) << 32) + a;
}

int main ()
{
    auto key_set1 = makeKey(21, 42);

    unordered_map<uint64_t, char> map;
    map[key_set1] = 'a';
    //...

    auto key_set2 = makeKey(42, 21);
    if(map[key_set1] == map[key_set2])
        std::cout << "success" << std::endl;
}

答案 3 :(得分:1)

由于订单在此处不重要,您可以将std::pair与自定义工厂一起使用以强制执行两个整数的顺序:

std::pair<int, int> make_my_pair(int x, int y) {
    return std::make_pair(std::min(x, y), std::max(x, y));
}

当然,如果您始终使用make_my_pair,这只会起作用。

或者,您可以定义自己的具有类似属性的密钥类。