擦除删除成语的性能增益来自何处

时间:2017-08-07 16:04:15

标签: c++ vector stl erase-remove-idiom

我需要从符合某个标准的向量中删除所有元素。

我的第一种方法是遍历向量并在符合条件的所有元素上调用vector :: erase。

据我所知,vector::erase对于这个用例有一个糟糕的表现,因为它从底层数组中删除了项目,并将向量的其余部分向前移动了一个元素(如果擦除则更多)一系列元素)。 当您移除多个元素时,后移元素将在每次移除时移动。

remove算法将所有元素移除,并将它们移动到向量的末尾,因此您只需要移除向量的后部,这不会移位。

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

不将元素移动到最后意味着像vector::erase一样向前移动所有后续元素?

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

2 个答案:

答案 0 :(得分:7)

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

如果您对要移除的每个元素使用erase,则需要在每个元素之后移动所有元素...每次调用erase。通常情况下,如果要删除k元素,则会将元素移动到最新元素之后(在向量中)k次,而不是仅移动一个元素。

但是如果你致电remove,你只会移动一次(见下面的例子)。

一个小例子,可以更好地理解这两种方法的工作原理:

  

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

erase对要移除的两个元素起作用:

  • 当您为第17个元素调用erase()时,您需要将元素18移动到999,982个元素。
  • 当您为第36个元素(它现在是第36个元素)调用erase()时,您需要将元素37移动到998,962个元素。

总的来说,你已经移动了962 + 982 = 1944个元素,其中962个已经被移动了两次。

使用remove,会发生以下情况:

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上的possible implementation,以便更好地了解std::remove的工作原理。

答案 1 :(得分:4)

优势在于std::remove不是一次只删除一个元素。例如,如果调用SELECT P, G, T FROM C_I l1 , C_II l2 WHERE ABS(l1.P-l2.P) < 0.005 AND ABS(l1.T-l2.T) < 20.0 AND ABS(l1.G-l2.G) < 0.005; 导致移除矢量的前10个元素,它会将第11个元素直接移动到第1个位置,第12个元素直接移动到第2个位置等等......然而,如果你一次擦除前10个元素,它会将你擦除的每个元素移回1.然后你将擦除下一个元素,每个元素都必须再次移动。对于每个被删除的元素,这都会重复。

此外,为了实现这一优势,删除的元素不必是顺序的。例如,如果要删除的调用会导致每个其他元素,从第一个开始,将被删除。首先,第二个元素将移动到第一个位置,这将留下两个元素的间隙,直到下一个可保持元素。然后第4个元素将直接移动到第2个位置,留下3个元素的间隙,依此类推。

另外,稍作修正:

  

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

删除算法不会这样做。它并不关心要删除的元素会发生什么。它们只是被要保留的元素所取代。未指定调用remove之后的结尾元素的值。您描述的算法是分区(具有反向比较功能)。