有没有更快的方法从无序集中删除和存储元素

时间:2019-01-30 10:34:18

标签: c++ performance optimization stl

我有一个如下的无序集合:

[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;

我本打算在擦除之前粘贴打印语句,但是许多答案都讨论了该问题。所以我将其保留。

2 个答案:

答案 0 :(得分:6)

否,std::unordered_set::erase成员函数是从集合中删除元素时唯一要使用的函数,并且docs说:

  

复杂度
  给定实例c为unordered_set:
  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";

答案 1 :(得分:5)

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

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


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

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


但是,擦除会使迭代器无效,因此擦除后直接通过它进行操作的行为是不确定的。