【问题标题】:Is there a faster way to remove and store an element from an unordered set有没有更快的方法从无序集中删除和存储元素
【发布时间】:2019-01-30 10:34:17
【问题描述】:

我有一个如下的无序集:

[1,2,3,4,6,7,5]

我想从我的无序集合中删除并存储一个元素,我不在乎删除了哪个元素。

我目前正在做以下事情。有更快的方法吗?

auto it = set_of_ints.begin();
set_of_ints.erase(it);
.....
.....
std::cout << "removed element is: " << *it << std::endl;

我的意思是在擦除之前粘贴打印语句,但许多答案都讨论了这个问题。所以我保持原样。

【问题讨论】:

  • 上面的代码在取消引用迭代器it时是UB。
  • @RichardHodges 我不小心重新排列了线条。我的意思是在擦除之前使用取消引用的值

标签: c++ performance optimization stl


【解决方案1】:

不,std::unordered_set::erase 成员函数是唯一用于从集合中擦除元素的函数,docs 说:

复杂性
给定一个 unordered_set 的实例 c:
1) 平均情况:常数,最坏情况:c.size()
[...]

那么为什么在最坏的情况下是c.size()?注意erase有一个返回值:

返回值
1-2) 最后一个被移除元素之后的迭代器。
[...]

函数必须找到“下一个元素”。 std::unordered_set 将其数据存储在所谓的桶列表中。理想情况下,这是与您擦除的元素相同的存储桶列表中的下一个可用插槽。最坏的情况是,它是其他存储桶中的最后一个可用插槽(因此它会随着容器的大小而缩放)。这取决于容器的插入/擦除历史。你可以看看libcxx实现here,有一个循环遍历桶列表中的节点(机制在@eeroika's answer中有很好的解释)。


除此之外,并非如此(也来自erase 上的文档):

对已擦除元素的引用和迭代器无效

因此,从集合中删除迭代器 it 后取消引用它是未定义的行为。你可以修复它

auto it = set_of_ints.begin();
const int value = *it;

set_ot_ints.erase(it);

std::cout << "removed element is: " << value << "\n";

【讨论】:

  • 我将前三行包含在一个范围内,以确保在 it 失效后不使用它,并使这一事实更加明显。
  • 顺便说一句,答案没有回答要删除的元素的选择。
  • 嗯...提高代码的可维护性并没有什么坏处:template&lt;class Container&gt; auto pop(Container const&amp; c) { auto const result = *begin(c) ; c.erase(begin(c)) ; return result; }
  • @Morpheus 这是您应该问的另一个问题,我们很乐意回答。但简而言之,std::unordered_setstd::vector更智能。这种聪明的代价是std::vector 不需要支付。擦除向量的最后一个元素几乎是免费的(不包括元素本身的破坏和大小计数器的递减)。
  • 最坏情况的平均复杂度是矛盾的:)
【解决方案2】:

不,没有比erase 更快的删除集合元素的方法了。除非您打算将元素转移到另一个集合中,否则extract 整体上可能更快。

元素的选择无关紧要;除非你手头没有迭代器,否则最快的迭代器是begin


如果您想知道擦除可能具有线性复杂性的情况:如果存储桶被实现为单链表(这是典型的),并且所有元素都具有相同的键(或者键恰好具有相同的哈希value) 并且被擦除的元素恰好是桶中的最后一个元素,则需要遍历整个容器。

恒定平均值假设键分布均匀且散列函数良好。


但是,擦除会使迭代器无效,因此在擦除后直接通过它的行为是未定义的。

【讨论】:

  • “我不在乎删除了哪个元素。” 但也许有些位置比其他位置更好。
  • @Jarod42 也许......不过,您必须对容器的内部结构有所了解。问题中没有提到这样的特殊知识。
  • @eerorika 我只想拥有最好的案例复杂度场景。我不确定unordered_set 的内部工作原理,但我可以假设这几乎是恒定的时间吗?
  • @Morpheus 最坏情况是线性的;但 lubgr 引用的平均值是恒定的。
猜你喜欢
  • 2013-10-11
  • 1970-01-01
  • 1970-01-01
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多