我已经读过,将项目添加到std::vector
末尾的时间复杂度是摊销常数,并且在std::deque
的顶部和底部插入项目是常量。因为这两个容器都具有随机访问权限迭代器因此访问任何索引处的元素是不变的。如果我有任何这些事实错误,请告诉我。我的问题是,如果访问std::vector
或std::deque
中的元素是常数,那么为什么通过擦除O(n)删除元素的时间复杂度)。其中一个答案here指出通过擦除元素是O(n)。我知道erase删除了起始迭代器和结束迭代器之间的元素,所以答案基本上意味着它的O(n)
取决于两个迭代器之间的元素的数量以及从vector / deque中删除单个元素任何指数都是零?
答案 0 :(得分:9)
std::vector
和std::deque
的情况略有不同,C ++ 98和C ++ 11也有所不同。
std::vector::erase()
的复杂性与擦除范围的长度和范围结束与容器末尾之间的元素数量呈线性关系(因此从末尾删除元素需要恒定的时间)。
C ++ 2003 [lib.vector.modifiers]
读取:
iterator erase(iterator position);
iterator erase(iterator first, iterator last);`
...
复杂性:
T
的析构函数被称为等于被删除元素数的次数, 但是T
的赋值运算符被称为等于删除元素后向量中元素数的次数。
C ++ 14草案N4140 [vector.modifiers]
读取:
复杂性:
T
的析构函数被称为等于元素数的次数 擦除,但T
的移动赋值运算符被称为等于数量的次数 擦除元素后向量中的元素。
所以你看到C ++ 11/14实现一般来说效率更高,因为它执行移动赋值而不是复制赋值,但复杂性仍然相同。
std::deque::erase()
的复杂度与擦除范围的长度和两个数字的最小呈线性关系:范围开始前剩余元素的数量和数量范围结束后剩余的元素。因此,从开头或结束删除元素需要不变的时间。
C ++ 2003 [lib.deque.modifiers]
:
iterator erase(iterator position);
iterator erase(iterator first, iterator last);
复杂性:对析构函数的调用次数与擦除的元素数相同,但是 对赋值运算符的调用数最多等于元素数的最小值 在擦除元素之前和擦除元素之后的元素数量。
C ++ 14草案N4140 [deque.modifiers]/5
:
复杂性:对析构函数的调用次数与擦除的元素数相同,但对赋值运算符的调用次数不再擦除元素之前的元素数量和擦除元素之后的元素数量中的较小者。
所以,它在C ++ 98和C ++ 11/14中是相同的,除了C ++ 11可以在移动赋值和复制赋值之间进行选择(这里我看到标准中有些不一致,因为措辞并没有像std::vector
那样提及移动分配 - 可能是另一个问题的原因。)
另请注意"最多"和#34;没有更多"在措辞中。这允许实现比线性更有效,但在实践中它们是线性的(DEMO)。
答案 1 :(得分:4)
删除向量中的元素是O(n),因为一旦删除元素,您仍需要移动所有连续元素以填充创建的间隙。如果一个向量有n个元素,那么在最坏的情况下你需要移动n-1个元素,因此复杂度为O(n)。
答案 2 :(得分:4)
删除元素确实是O(n)
,不是因为你需要做些什么才能找到要删除的元素,而是因为你必须对之后的所有做什么。这些元素需要向下滑动以填补空位。
因此,平均而言,擦除将在向量的一半处占据一个元素,因此您必须移动大约一半的元素。因此O(n)
。最好的情况是,你删除最后一个元素 - 不需要滑动。最糟糕的情况是,删除第一个元素 - 然后必须移动每个其他元素。
答案 3 :(得分:0)
通过这种方式,时间复杂度=范围长度+移位长度(n-范围末端)
vector<int> it = (vector<int>::iterator) &vec[pos];
vec.erase(it, it+length);