迭代 unordered_map 的复杂性是啥

Posted

技术标签:

【中文标题】迭代 unordered_map 的复杂性是啥【英文标题】:what is the complexity of iterating an unordered_map迭代 unordered_map 的复杂性是什么 【发布时间】:2021-06-30 18:47:21 【问题描述】:

迭代使用散列和链接实现的 unordered_map 的复杂性是多少?具体来说,这是否涉及遍历所有存储桶,在这种情况下,复杂性是 O(Buckets + NElements) 与理想的,例如O(NElements)

【问题讨论】:

来自iterator.requirements.general,“迭代器的所有类别只需要在恒定时间内(摊销)对给定类别可实现的那些功能。因此,迭代器的需求表和概念定义没有指定复杂。”所以增量是 O(1),穿过整个容器是 O(N)。 @1201ProgramAlarm:该要求似乎与迭代器适配器(以及范围提案中的过滤器等)不兼容。 【参考方案1】:

遍历unordered_map 的复杂度是多少?

O(N) 其中 N 是存储的元素数。标准的most relevant part:

迭代器的所有类别只需要在恒定时间内(摊销)对给定类别可实现的那些函数。因此,迭代器的需求表没有复杂度列。

您可以在例如this answer

..使用散列和链式实现?

所有无序映射都使用链式散列 - 请参阅 here。

具体而言,这是否涉及遍历所有存储桶,在这种情况下,复杂度是 O(Buckets + NElements) 与理想的,例如O(N个元素)

它完成了您所说的“理想”。在 GCC 的情况下,桶将迭代器保存到一个单链表中,并且在迭代容器时通常会遍历更短的列表。通常是因为即使不更改默认的max_load_factor() 1.0,桶的数量也有可能完全等于元素的数量——任何比这更多的元素都会触发调整大小。但是,当几乎所有元素都被删除时,迭代的大 O 效率变得更加重要,因为桶的数量不会自动向下调整(即减少)。

【讨论】:

感谢 Tony,有了单链表,大概那里的节点有一个链接返回到它们所属的存储桶以处理删除? @user3882729:好问题。实际上,GCC的无序容器迭代器只有一个指向节点的指针,而节点只有一个指向next-node和storage的指针,但是在“storage”中既有值对象又有哈希码,所以可以划分通过桶计数预先计算的哈希值来找到桶。您可以在 gdb 中轻松看到:(gdb) print *it._M_cur $2 = [...]<std::__detail::_Hash_node_base> = _M_nxt = 0x41a330, _M_storage = [...] __data = "\006\000\000", __align = <No data fields>, _M_hash_code = 6

以上是关于迭代 unordered_map 的复杂性是啥的主要内容,如果未能解决你的问题,请参考以下文章

unordered_map cbegin()+ number //常量复杂度?

unordered_map 迭代器会改变吗?

为啥允许 std::unordered_map::rehash() 使迭代器无效?

unordered_map 迭代器指向 end(),如何从 unordered_map 中检索键?

为啥我不能增加 std::unordered_map 迭代器?

unordered_map 迭代器迭代到空指针,无视可能性