C ++ STL unordered_map如何解决冲突?

问题描述 投票:48回答:2

C ++ STL unordered_map如何解决冲突?

看看http://www.cplusplus.com/reference/unordered_map/unordered_map/,它说“独特的键容器中没有两个元素可以有相同的键。”

这应该意味着容器确实解决了碰撞。但是,该页面并没有告诉我它是如何做到的。我知道一些解决冲突的方法,比如使用链表和/或探测。我想知道的是c ++ STL unordered_map如何解析它。

c++ stl unordered-map
2个回答
63
投票

该标准比大多数人似乎意识到的更多地定义了这一点。

具体而言,该标准要求(§23.2.5/ 9):

无序关联容器的元素被组织成桶。具有相同哈希码的密钥出现在同一个存储桶中。

界面包括一个恒定运行的bucket_count。 (表103)。它还包括一个bucket_size,必须按时间线性运行桶的大小。

这基本上描述了使用冲突链的实现。当您使用碰撞链时,满足所有要求介于简单和简单之间。 bucket_count()是数组中元素的数量。 bucket_size()是碰撞链中的元素数量。分别使它们处于恒定和线性时间是简单而直接的。

相比之下,如果使用线性探测或双重散列等方法,那些要求几乎不可能满足。具体而言,所有散列到特定值的项目都需要在同一个存储桶中着陆,并且您需要能够在恒定时间内对这些存储区进行计数。

但是,如果你使用线性探测或双重散列之类的东西,找到散列到相同值的所有项目意味着你需要散列值,然后遍历表格中非空项目的“链”以查找有多少那些哈希值相同。但是,对于散列到相同值的项目数,这并不是线性的 - 它与散列到相同或冲突值的项目数呈线性关系。

有了足够的额外工作和相当多的要求将某些要求的含义拉伸到断点,几乎不可能使用除碰撞链之外的其他东西来创建哈希表,并且至少仍然满足要求 - - 但我不确定它是否可能,而且肯定会涉及到相当多的额外工作。

总结:std::unordered_set(或unordered_map)的所有实际实现无疑都使用碰撞链。虽然可能(几乎不可能)使用线性探测或双重散列来满足要求,但这样的实现似乎失去了很多并且几乎没有任何回报。


0
投票

我找到了这个答案,寻找如何检测我的类型何时发生碰撞,所以我会发布这个以防问题的意图:

我相信有一些误解,“独特的密钥容器中没有两个元素可以拥有相同的密钥。”

看下面的代码

//pseudocode
std::unordered_map<int, char> hashmap;
hashmap[5] = 'a';
hashmap[5] = 'b'; //replace 'a' with 'b', there is no collision being handled.

我认为Jerry的答案是指它用来将键缩小到适当的数组索引的内部系统。

如果你想要为你的类型处理碰撞(使用存储桶),你需要std::unordered_multimap并且必须迭代

希望这个代码可以在没有我生成它的上下文的情况下读取。它基本上检查以查看与散列关联的存储桶中的任何元素是否是我正在寻找的元素。

//sp is std::shared_ptr
//memo is std::unordered_multimap< int, sp<AStarNode> >

//there's probably multiple issues with this code in terms of good design (like using int keys rather than unsigned)

bool AStar_Incremental::hasNodeBeenVisited(sp<AStarNode> node)
{
    using UMIter = std::unordered_multimap<int, sp<AStarNode> >::iterator;

    bool bAlreadyVisited = false;

    //get all values for key in O(1*)
    int hash = WorldGrid::hashGrid(node->location);
    std::pair<UMIter, UMIter> start_end = memo.equal_range(hash); //bucket range
    UMIter start = start_end.first;
    UMIter end = start_end.second;

    //hopefully this is implemented to be O(m) where m is the bucket size.
    for(UMIter bucketIter = start; bucketIter != end; ++bucketIter)
    {
        sp<AStarNode> previousNode = bucketIter->second;
        sf::Vector2i& previousVisit = previousNode->location;
        if (previousVisit == node->location)
        {
            bAlreadyVisited = true;
            break;
        }
    }

    return bAlreadyVisited;
}
© www.soinside.com 2019 - 2024. All rights reserved.