根据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()
作为插入提示同样好(并且显然更安全)。然而,这显然只是一个经验观察。如上所述,标准在这方面仍然令人困惑。
答案 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次插入的结果如下(显示系统时间,因为在这种情况下其他值并不特殊):
end()
: 5.71秒 --end()
:5.84秒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;
}