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

How does C++ STL unordered_map resolve collisions?

本文关键字:解决 冲突 map 如何 unordered C++STL      更新时间:2023-10-16

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

看着 http://www.cplusplus.com/reference/unordered_map/unordered_map/,它说"唯一键 容器中没有两个元素可以具有等效的键。

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

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

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

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

该接口包括一个在恒定时间内运行的bucket_count。(表103)。它还包括一个bucket_size,该必须根据存储桶的大小在时间上线性运行。

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

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

但是,如果您使用线性探测或双重哈希之类的方法,则查找所有哈希为相同值的项目意味着您需要对值进行哈希处理,然后遍历表中非空项目的"链",以查找其中有多少哈希为相同的值。不过,对于散列到相同值的项目数,这不是线性的 - 对于散列到相同冲突值的项目数是线性的。

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

总结:std::unordered_set(或unordered_map)的所有实际实现无疑都使用冲突链。虽然使用线性探测或双哈希可能(只是勉强)可以满足要求,但这样的实现似乎损失了很多,几乎没有任何回报。

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

我相信对"容器中没有两个元素可以具有等效键"存在一些误解。

看看下面的代码

//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;
}