C++ 擦除-删除习惯用法的性能增益来自哪里

C++ 擦除-删除习惯用法的性能增益来自哪里,c++,vector,stl,erase-remove-idiom,C++,Vector,Stl,Erase Remove Idiom,我需要删除向量中满足特定条件的所有元素 我的第一种方法是循环遍历vector,并对满足条件的所有元素调用vector::erase 据我所知,vector::erase在这个用例中的性能很差,因为它从底层数组中删除了项,并将向量的其余部分向前移动了一个元素(如果删除了一系列元素,则会向前移动多个元素)。 拆卸多个元件时,每次拆卸时后元件都会移动 remove算法获取所有要删除的元素,并将它们移动到向量的末尾,因此只需删除向量的后部,而不需要移动 但为什么这比擦除更快?(更快吗?) 将一个元素移到

我需要删除向量中满足特定条件的所有元素

我的第一种方法是循环遍历vector,并对满足条件的所有元素调用vector::erase

据我所知,
vector::erase
在这个用例中的性能很差,因为它从底层数组中删除了项,并将向量的其余部分向前移动了一个元素(如果删除了一系列元素,则会向前移动多个元素)。 拆卸多个元件时,每次拆卸时后元件都会移动

remove
算法获取所有要删除的元素,并将它们移动到向量的末尾,因此只需删除向量的后部,而不需要移动

但为什么这比擦除更快?(更快吗?)

将一个元素移到末尾不意味着像在
vector::erase
中那样向前移动以下所有元素吗


为什么删除的复杂性只有O(n)?

优点在于
std::remove
不只是一次删除一个元素。例如,如果对
std::remove
的调用导致删除向量的前10个元素,则会将第11个元素直接移动到第1个位置,将第12个元素直接移动到第2个位置,以此类推。。。然而,如果一次擦除一个前10个元素,它会将擦除后的每个元素向后移动1。然后删除下一个元素,每个元素都必须再次移动。这会对每个被擦除的元素重复

此外,删除的元素不必是连续的,这一优势就可以实现。例如,如果对remove的调用导致从第一个元素开始的所有其他元素都被删除。首先,第二个元件将移动到第一个位置,这将留下两个元件的间隙,直到下一个可保持元件。然后第4个元素将直接移动到第2个位置,留下3个元素的间隙,依此类推

还有,稍微更正一下:

移除算法获取所有要移除的元素,并将它们移动到向量的末尾


删除算法不能做到这一点。它不关心要删除的元素发生了什么。它们只是被保留下来的元素所取代。未指定调用remove后末尾元素的值。您描述的算法是分区(使用反向比较函数)。

这里的性能问题不是删除要删除的元素,或者将它们移动到末尾(实际上不会发生),而是移动要保留的元素

如果对要删除的每个元素使用
erase
,则需要移动这些元素之后的所有元素。。。对于每次调用
擦除
。通常,如果要删除
k
元素,将在最新的元素(向量中)之后移动元素
k
次,而不是仅移动一次

但是,如果调用
remove
,则只会移动它们一次(请参见下面的示例)

下面是一个小示例,可以更好地了解这两种方法的工作原理:

假设有一个大小为1000的向量,要删除的元素位于位置17和37

通过
erase
作用于要删除的两个元件:

  • 为第17个元素调用
    erase()
    时,需要将元素18移动到999982个元素
  • 为第36个元素调用
    erase()
    (现在是第36个!)时,需要将元素37移动到998962个元素
总的来说,您已经移动了962+982=1944个元素,其中962个元素已经被免费移动了两次

使用
删除
,情况如下:

element 0 does not change;
element 1 does not change;
...
element 17 is "discarded";
element 18 is moved at position 17;
element 19 is moved at position 18;
...
element 36 is moved at position 35;
element 37 is "discarded";
element 38 is moved at position 36;
...
element 999 is moved at position 997.
总共移动了998个元素(1000减去删除的两个元素),这比前面方法中的1943个元素要好得多。如果要删除的元素超过2个,则效果更好

您可以查看en.cppreference.com上的,以更好地了解
std::remove
的工作原理。

据我所知,vector::erase在这个用例中的性能很差——如果编码器在每次迭代中没有正确地重新安装迭代器,导致删除,那么它也是一个错误源。这只是应该使用
remove/erase
的另一个原因。