STL矢量元素去除效率

时间:2014-05-22 09:00:14

标签: c++ stl

我有一个对象矢量。所有对象元素都是唯一的。 Functor是为类的唯一id定义的。

从向量

中删除对象元素会更有效
myvec.erase(std::remove_if(myvec.begin(), myvec.end(), MyClass(id)), myvec.end());

//OR

itonmyvec = std::find_if(myvec.begin(), myvec.end(), MyClass(id));
if(itonmyvec != myvec.end())
    myvec.erase(itonmyvec);

5 个答案:

答案 0 :(得分:4)

实际上最快的方法都不是。两者都受到保留其余元素相对顺序的标准中的额外约束,但这不是你所要求的。

因此,最快的解决方案是

  1. 找到元素
  2. 将其与最后一个元素交换
  3. 删除新的最后一个元素。
  4. 执行O(1)分配,而不是O(N)。

答案 1 :(得分:2)

当您使用向量时,两个建议的实现几乎完全相同。

第一个将遍历向量一次,测试每个元素并同时压缩向量,然后最终破坏最后一个元素并缩短向量。

第二个将遍历向量,直到找到元素,然后遍历向量的其余部分以压缩并最终缩短向量。

事实证明,两个版本之间唯一真正的区别是,第二个版本能够保存平均O(n / 2)比较。如果比较便宜,性能将更多地取决于系统的其他属性,例如:哪个实现更加缓存友好。

因此,最终答案只能是基准测试!

PS:第一个版本必须检查remove_if的结果是否与向量的end完全等效,以便在两个版本中检查完成(或不完成)方式。

答案 2 :(得分:1)

remove_if的文档说

  

从范围...

中删除满足特定条件的所有元素

find_if指定为

  

返回范围中的第一个元素......

由于您知道矢量中的所有元素都是唯一的,因此您可以在第一次匹配后安全地停止搜索。这意味着对于这种特定情况,第二种形式可能更快。

另请注意,正如评论中所述,第一种方法是错误的。 remove_if将迭代器返回到(可能更新的)范围的末尾。您不希望从矢量中删除它 - 保证不会与您的目标值匹配。

答案 3 :(得分:0)

对于MSalters给出的答案,我已经写下了代码。我无法将代码添加到评论中,因此将其添加到新答案中。希望有所帮助!

//Remove 3rd element

vector<int> v {1,2,3,4,5};          //C++11 compiler needed!

vector<int>::iterator it1 = find(v.begin(), v.end(), 3); 

vector<int>::iterator it2 = --v.end();

std::swap(*it1, *it2);

v.pop_back();

答案 4 :(得分:0)

你可以做得比当前接受的答案更好,如果你愿意牺牲异常安全:不是用最后一个元素交换然后调用pop_back(),你可以简单地覆盖要使用最后一个元素删除的元素。你必须向后迭代,从最后一个元素开始。

如果你使用整数,它很好,最有可能节省时间。如果你与谁一起工作,谁知道什么类可能会移动副本,那么你有麻烦,你的异常安全保证......

以下是一个示例代码:

#include <iostream>
#include <vector>

template <typename Vector, typename Pred>
void remove(Vector& v, Pred p) {

  auto last(v.rbegin());

  for (auto pos(last), rend(v.rend()); pos!=rend; ++pos) {

    if (p(*pos))
      // overwrite the element to be removed with the currently last element
      *pos = std::move(*last++);
  }

  v.erase(last.base(), v.end());  
}

// pretty print the vector for debugging
template <typename Vector>
void show(const Vector& v) {

  std::cout << "{ ";

  auto pos(v.begin());

  auto last(v.end());

  if (pos!=last) {

    --last;

    while (pos!=last)
      std::cout << *pos++ << ", ";

    std::cout << *last;
  }

  std::cout << " }" << std::endl;
}

// a simple-minded test that removes all 2s from the vector
void remove_2s(std::initializer_list<int> l) {

  auto equals_two = [](int i) { return i==2;};

  std::vector<int> v{l};

  remove(v, equals_two);

  show(v);
}

int main() {

  remove_2s({1, 2, 3, 2, 5, 7, 4, 2, 6});

  remove_2s({ 1, 2 });

  remove_2s({ 2, 1 });  

  remove_2s({ 2 });

  remove_2s({ 2, 2 });  

  remove_2s({  });

  remove_2s({ 1 });
}