std :: unordered_set :: erase复杂性

时间:2016-01-01 14:24:53

标签: c++ c++11 stl containers unordered

我不明白,为什么在最坏的情况下(其中N是元素的数量) std :: unordered_set O(N)复杂度的擦除方法?标准(n4296)表示 erase 方法的所有三个版本在最坏的情况下具有O(a.size())复杂度( a 是容器),并且仅使迭代器无效指向已擦除的元素,但不是所有迭代器(即不进行重新擦除)。即使对于采用一个迭代器参数并且在平均情况下具有恒定复杂性的版本也是如此。我认为这是因为 erase 版本返回到下一个元素的迭代器,这需要在擦除元素之后找到第一个非空桶,并且它给出O(a.bucket_count())复杂度,但不是O(a.size())!元素数量与桶数的比例不成正比。例如:

#include <iostream>
#include <unordered_set>
using namespace std;

int main()
{
    std::unordered_set<int> aSet;
    aSet.insert ({125, 126});
    aSet.rehash (1000);
    cout << aSet.size() << endl;
    cout << aSet.bucket_count() << endl;
}

输出

Size: 2
Bucket count: 1031

容器的大小只有2而bucket_count是1031.当调用 erase 方法时,它将寻找下一个非空桶,它可以放在最后,即复杂性是O(a.bucket_count()),但不是O(a.size())。什么是标准给出O(a.size())复杂性的原因?

3 个答案:

答案 0 :(得分:4)

  

即使对于带有一个迭代器参数的版本也是如此   在一般情况下具有不变的复杂性。

无序关联容器具有前向迭代器 - 允许通过单链表实现它们。

擦除节点涉及将节点重新链接到节点之后的节点。在迭代器指向的节点之前找到节点可能是基于单链接列表的实现中的最坏情况O(N),因为您基本上必须遍历存储桶(可以包含容器中的每个元素)在完全碰撞的情况下)。

答案 1 :(得分:3)

最明显的原因是退化散列函数可能会为所有元素产生相同的值。结果他们都被分到同一个桶里。尽管不太可能,但即使具有相当好的散列函数,也可能发生相同的 ,尤其是在将值映射到桶之后。由于没有合理的散列函数质量规范,标准不能强制要求更好的时间复杂度。

答案 2 :(得分:3)

  

标准给出O(a.size())复杂度的原因是什么?

std::unordered_set是一个哈希容器。如果提供的散列函数映射到插入容器的每个元素的相同值,则它们将被链接在一起(可能在链表中)。因此,在最坏的情况下,单个“列表”可以包含容器中的所有项目,就像任何“查找”操作一样,erase在元素数量上更加线性。