为什么unordered_set不提供数组访问运算符

时间:2015-04-10 01:56:33

标签: c++ stl unordered-set

我很好奇为什么平均随机访问具有恒定时间复杂度的STL容器unordered_set为什么不提供一种方法来访问容器中距离第一个元素一定距离的元素。例如:

T& unordered_set::operator[](size_t index)
{
    return *(begin() + index);
}

4 个答案:

答案 0 :(得分:5)

“远距离”访问元素意味着有一些有意义的方法来测量该距离。问题std::unordered_set就是无序。因此,没有任何有意义的方式以非任意的方式解释“与起点有一定距离”。

如果您想通过距离访问,请将数据复制到矢量中:

std::vector tmp(unordered.begin(), unordered.end());

答案 1 :(得分:5)

unordered_set被实现为哈希表,其中有随机可访问的桶#34;每个有效地包含0个或更多元素的链表。因此,存储数字1到7的unordered_set的快照可能看起来像这样(元素的确切位置取决于所使用的散列函数,所以这只是说明性的):

buckets    linked-list of elements
[0]        1 --> 5 --> nullptr
[1]        nullptr
[2]        4 --> nullptr
[3]        nullptr
[4]        nullptr
[5]        7 --> nullptr
[6]        6 --> 3 --> 2 --> nullptr
[7]        nullptr

正如您所看到的,没有简单的方法来推进n元素...您基本上必须关注链接列表,当您找到nullptr时跳到下一个存储桶。这就是为什么begin()操作无法+ n次移动返回一个随机访问迭代器O(1)次的原因(它只提供一个前向迭代器) )......

所以当你问......

  

unordered_set,平均随机访问时间复杂度

...我认为您通过按键随机访问混淆随机访问。你可以在O(1)摊销的常数时间内找到任何给定的密钥,但找到第n个元素是O(n)。

(注意:C ++ 11标准不允许实现自由选择closed hashing (aka open addressing)来实现unordered_set ......在构建后max_load_factor显而易见必须为1.0,并且insert / emplace迭代器失效期间的规则只能在超出max_load_factor时发生。)

答案 2 :(得分:2)

根据定义,

unordered_set 无序,因此通过索引访问它并不是非常有用。每次插入时,任何特定元素的索引都可能会发生变化。

另外,根据this referenceunordered_set的迭代器是前向迭代器,而不是随机访问。

答案 3 :(得分:1)

为了为项目成员资格检查提供恒定的摊销时间,这是无序集合的优势,对于某些任意项目类型的一般情况,它必须实现为哈希表。

在常数时间内,确保散列表中的每个键(项)也由链表的节点引用并不困难。这提供了一种以未指定的顺序迭代表中的项的方法。但是,转到链接列表中的 i 项目是线性时间。

有一个妥协的解决方案,当每个项目添加到哈希表时,它也会添加到已排序的树中。这要求项目具有可比性,并且增加了对数的添加和删除复杂性(保持恒定的检查时间)。但是虽然这支持在对数时间内访问 i -th项目,但是哪个项目是 i - 会有所不同,并且对此功能的需求确实不大

关键是C ++ 11在无序容器中需要O(1)平均时间,这与有序树不兼容。

因此,由于直接索引是不切实际的(线性时间)而不是需求,因此不提供,但您可以始终使用*std::next( s.begin(), i )作为假设s[i]的线性时间替代方法。原则上,您可以通过将其复制到std::vector来优化不会发生变化的集合。但在大多数情况下,使用迭代器会更好。