我有一个如下的无序集合:
[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;
我本打算在擦除之前粘贴打印语句,但是许多答案都讨论了该问题。所以我将其保留。
答案 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
。
如果您想知道擦除可能具有线性复杂度的情况:如果将存储桶实现为单链表(通常),并且所有元素都具有相同的键(或者这些键碰巧具有相同的哈希值)值),而被擦除的元素恰好是存储桶中的最后一个元素,则需要遍历整个容器。
恒定平均值假设键分布均匀且散列函数良好。
但是,擦除会使迭代器无效,因此擦除后直接通过它进行操作的行为是不确定的。