尽管有关于vector的remove_if + erase有几十个问题。 我无法找到这种行为的表现。 我写的时候:
myVector.erase(remove_if(myVector.begin(),
myVector.end(),
some_predicate), myVector.end());
remove if会将迭代器返回到最后一个相关项+ 1(让它称之为X)。 我相信这会发生在O(n)。
但擦除将如何工作?
感谢。
答案 0 :(得分:19)
考虑这个载体:
|0|1|2|3|4|5|6|7|8|9|
我们使用remove_if
删除所有4的倍数的元素:
std::remove_if(v.begin(), v.end(), [](auto i){ return i != 0 && !(i%4); });
这开始使用迭代器X迭代向量,直到找到谓词返回true的元素:
|0|1|2|3|4|5|6|7|8|9|
X
这是我们要删除的第一个元素。
接下来,它创建另一个指向下一个元素的迭代器,Y = X + 1,并检查谓词* Y:
|0|1|2|3|4|5|6|7|8|9|
X Y
谓词是假的,所以我们想保留那个元素,所以它通过*X = std::move(*Y)
将{x}分配给我们要删除的元素的下一个元素:
|0|1|2|3|5|5|6|7|8|9|
X Y *X = std::move(*Y)
所以我们有两个迭代器,X和Y,其中X指向“output”中的下一个元素(即我们没有删除的元素),Y是下一个要考虑删除的元素。
我们将两个迭代器移动到下一个位置,检查Y的谓词(再次为false),然后执行另一个赋值:
|0|1|2|3|5|6|6|7|8|9|
X Y *X = std::move(*Y)
然后它在下一个位置再次做同样的事情:
|0|1|2|3|5|6|7|7|8|9|
X Y *X = std::move(*Y)
然后它继续前进,但发现谓词对于Y
是正确的|0|1|2|3|5|6|7|7|8|9|
X Y
所以它只增加Y,跳过该元素,因此不会将其复制到X的“输出”位置:
|0|1|2|3|5|6|7|7|8|9|
X Y
对于Y,谓词不正确,因此它将其分配给X:
|0|1|2|3|5|6|7|9|8|9|
X Y *X = std::move(*Y)
然后再次增加X和Y
|0|1|2|3|5|6|7|9|8|9|
X Y
现在Y是过去的结果,所以我们返回X(它指向输出序列的末尾,即我们想要保留的元素)。
在remove_if
返回X之后,我们调用v.erase(X, v.end())
,因此向量会调用从X到结尾的每个元素的析构函数:
|0|1|2|3|5|6|7|9|~|~|
X end
然后设置大小,使向量结束于X:
|0|1|2|3|5|6|7|9|
end
在此v.capacity() >= v.size()+2
之后,因为两个最终元素使用的内存仍然存在,但未使用。
答案 1 :(得分:8)
vector::erase
的复杂性为well-defined:
线性:对T的析构函数的调用数与擦除的元素数相同,T的赋值运算符被称为等于擦除元素后向量中元素数的次数
它在内部工作的方式(即如何删除你的元素)是无关紧要的。
remove_if
的复杂性也是defined,而且是
谓词的std :: distance(first,last)应用程序。
因此,您的代码具有线性复杂性。
答案 2 :(得分:0)
为什么不使用swap' n pop方法?我们在向量中优化擦除时愚弄了很多,发现这是最快的,因为它具有O(1)
复杂度。唯一的缺点是它没有保持秩序。很多情况都很好。以下是此类操作的模板方法:
template<typename T>
inline typename std::vector<T>::iterator unorderedErase(std::vector<T>& p_container,
typename std::vector<T>::iterator p_it)
{
if (p_it != p_container.end() - 1)
{
std::swap(*p_it, p_container.back());
p_container.pop_back();
return p_it;
}
// else
p_container.pop_back();
return p_container.end();
}