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

时间:2014-02-03 02:01:17

标签: c++ stl unordered-map

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

查看http://www.cplusplus.com/reference/unordered_map/unordered_map/,它显示“唯一键 容器中没有两个元素可以具有等效键。“

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

2 个答案:

答案 0 :(得分:62)

该标准对此的定义比大多数人似乎意识到的更多。

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

  

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

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

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

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

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

如果有足够的额外工作和相当多的延伸几乎要达到突破点的某些要求的含义,那么使用碰撞链以外的东西创建哈希表几乎是不可能的,并且至少仍然可以满足要求 - 但我不确定它是否可能,而且肯定会涉及相当多的额外工作。

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

答案 1 :(得分: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;
}