C ++ 0x问题:将时间插入到std :: set中

时间:2011-07-03 11:48:34

标签: c++ stl insert set complexity-theory

根据this page,如果我使用

,我可以实现恒定时间插入
iterator std::set::insert ( iterator position, const value_type& x );

和我提供的position迭代器直接“在”正确(有序)插入点之前。

现在我关心的情况是,如果我知道我插入的值最后(因为它是最大的),例如:

set<int> foo = {1, 2, 3};
foo.insert(4); // this is an inefficient insert

根据上述标准,我应该将最后一个元素foo.end()-1传递给insert 而不是 foo.end()。我的理解是否正确?如果我通过foo.end()会怎样?它是O(log n)插入还是O(1)插入。所以,选项是:

// Option A
foo.insert(foo.end()-1, 4);

// Option B
foo.insert(foo.end(), 4);

// Safer version of Option A
if(foo.empty())
    foo.insert(4);
else
    foo.insert(foo.end()-1, 4);

我问,因为我正在编写一个在容器上模板化的函数。我想在传入任何容器的末尾插入一个元素(我知道是最大的)。上面的“选项A”对于像vector这样的容器有不同的行为:

foo.insert(foo.end()-1, 4);
// result is {1, 2, 3, 4} if foo is an std::set
// result is {1, 2, 4, 3} if foo is an std::vector

正如@Bo_Persson所暗示的那样,这里的问题是C ++ 03说“一般是对数,但如果在p之后插入t,则摊销常数。”而C ++ 0x表示“一般为对数,但如果在p之前插入t,则摊销常数。”

PS:我在Ubuntu 11.04上使用GCC 4.5并启用了C ++ 0x支持。

编辑:我运行了经验测试,启用并禁用了C ++ 0x支持和posted the results in an answer。基本上,结论是提供end()作为插入提示同样好(并且显然更安全)。然而,这显然只是一个经验观察。如上所述,标准在这方面仍然令人困惑。

4 个答案:

答案 0 :(得分:4)

运行测试而不是阅读库规范是否是作弊?

对于g++-4.4 -O2整数0 <= i < 5000000我的运行时间 标准插入

real    0m14.952s
user    0m14.665s
sys 0m0.268s

和我使用end()作为提示插入的运行时间是

real    0m4.373s
user    0m4.148s
sys 0m0.224s

end() - 1的插入速度与我所说的一样快,但使用起来比较麻烦,因为end() - 1是非法操作(你必须使用operator--())和如果集合恰好为空,它会崩溃。

#include <set>

typedef std::set<int> Set;

void insert_standard(Set& xs, int x)
{
    xs.insert(x);
}

void insert_hint_end(Set& xs, int x)
{
    xs.insert(xs.end(), x);
}

int main()
{
    const int cnt = 5000000;
    Set xs;
    for (int i = 0; i < cnt; i++) {
        // insert_hint_end(xs, i);
        insert_standard(xs, i);
    }
}

答案 1 :(得分:3)

position是否应该在插入点之前或之后指向尚不完全清楚。有些实现可以使用。

另一方面,如果您想要不同容器的不同行为,为什么不为您的函数编写两个重载,一个用于具有push_back函数的容器和一个用于std::set的容器。

答案 2 :(得分:2)

只提供在新值之后立即的迭代器才有意义。

那是因为在N个元素的集合中,有N + 1个可能的插入点。存在一个迭代器,之后的值高于任何预先存在的元素,但是没有迭代器出现在所有元素之前的值之前。

答案 3 :(得分:1)

跟随@antonakos的脚步,我正在扩展“作弊”解决方案并进行实证测试。我正在使用GCC 4.5进行优化(-02)并考虑未启用C ++ 0x支持的情况以及何时使用-std=c++0x。 40,000,000次插入的结果如下(显示系统时间,因为在这种情况下其他值并不特殊):

  • 没有C ++ 0x支持
    • 没有提示:26.6秒
    • 提示end() 5.71秒
    • 提示--end():5.84秒
  • 启用C ++ 0x支持
    • 没有提示:29.2秒
    • 提示end() 5.34秒
    • 提示--end():5.54秒

结论:当end()作为插入提示提供时,GCC(启用或不启用C ++ 0x)会有效插入。

我使用的代码基于@ antonakos的:

#include <set>
typedef std::set<int> Set;

void insert_standard(Set & xs, int x) {
    xs.insert(x);
}

void insert_hint_end(Set & xs, int x) {
    xs.insert(xs.end(), x);
}

void insert_hint_one_before_end(Set & xs, int x) {
    xs.insert(--xs.end(), x);
}

int main() {
    const int cnt = 40000000;
    Set xs;
    xs.insert(0);
    for (int i = 1; i < cnt; i++) {
        //insert_standard(xs, i);
        //insert_hint_one_before_end(xs, i);
        insert_hint_end(xs, i);
    }

    return 0;
}