为什么 std::list.size() 不是恒定时间?

Why isn't std::list.size() constant-time?

本文关键字:定时间 std list size 为什么      更新时间:2023-10-16

此代码运行0.012秒:

 std::list<int> list;
 list.resize(100);
 int size;
 for(int i = 0 ; i < 10000; i++)
     size = list.size();

这是9.378秒:

 std::list<int> list;
 list.resize(100000);
 int size;
 for(int i = 0 ; i < 10000; i++)
     size = list.size();

我认为,可以以这种方式实现std ::列表,该大小将存储在私人变量中,但根据此,每次我调用大小时都会再次计算。谁能解释为什么?

恒定时间size()与恒定时间list.splice之间存在冲突。委员会选择赞成splice

当您在两个列表之间拼接节点时,您必须计算移动的节点以更新两个列表的尺寸。通过更改一些内部指针,这带走了剪接节点的很多优势。


正如评论中指出的那样,C 11通过放弃o(1)的 splice使用(?)的使用来改变这一点。

void splice(const_iterator position, list& x, const_iterator first, const_iterator last);
void splice(const_iterator position, list&& x, const_iterator first, const_iterator last);

复杂性:如果 &x == this,则恒定时间;否则,线性时间。

ISO/IEC 14882:2011,§c.2.12,第23条:"容器库":

更改:大小()成员函数的复杂性现在恒定

基本原理:缺乏大小()复杂性的规范,导致了具有不一致的性能特征的不同实现。

对原始功能的影响:符合C 2003的某些容器实现可能不符合本国际标准中指定的大小()要求。调整诸如STD ::列表之类的容器列表到更严格的要求可能需要不兼容的更改。


评论:

在23.3.5.5中 - "列表操作",再次在ISO/IEC 14882:2011中:

列表提供了三个剪接操作,这些剪接操作将元素从一个列表转移到另一个列表。如果get_allocator()!= x.get_allocator()。

void splice(const_iterator位置,列表&amp; x);
void splice(const_iterator位置,列表&amp; x);
需要:&amp; x!= this。
效果:插入位置之前的X内容,X变为空。指针和提及X的移动元素现在指的是相同的元素,但是 *此元素。迭代器引用移动元素将继续提及其元素,但现在它们以迭代剂的形式行为 *,而不是x。
复杂性:恒定时间。

void剪接(const_iterator位置,列表&amp; x,const_iterator i);
void splice(const_iterator位置,列表&amp; x,const_iterator i);
效果:插入i在位置之前从列表x指向的元素,然后从x中删除元素。如果位置== i或位置== i,则结果是不变的。指针和引用 *我继续指代相同的元素,但作为 *的成员。迭代器 *i(包括i本身)继续指的是相同的元素,但现在以迭代剂的形式行为 *,而不是x。
需要:我是X。
的有效解释迭代器 复杂性:恒定时间。

void splice(const_iterator位置,列表&amp; x,const_iterator first,const_iterator last);
void splice(const_iterator位置,列表&amp; x,const_iterator first,const_iterator last);
效果:在位置之前将元素插入[首先,最后一个),然后从x中删除元素。 需要:[第一个,最后一个)是x中的有效范围。结果如果位置是该范围内的迭代器,则结果是不确定的。指针和提及X的移动元素现在指的是相同的元素,但是 *此元素。提到移动元素的迭代器将继续指向其元素,但是现在它们以迭代器的形式行为 *,而不是x。
复杂性:如果&amp; x == this;否则,线性时间。