优雅的方法来删除另一个向量中包含的向量的所有元素?

时间:2014-01-17 20:25:22

标签: c++ stl

在查看某些代码时,我发现std :: set_difference的循环和算法缓慢的实现 :

 for(int i = 0; i < a.size(); i++)
 {
  iter = std::find(b.begin(),b.end(),a[i]);
  if(iter != b.end())
  {
     b.erase(iter);
  }
 }

可以很容易地用sort(向量没有排序)+ set_difference来替换,但是这需要分配新的内存(参见我最近的Q Can output of set difference be stored in first input?为什么它不能完成“inplace”)。
所以我的解决方案就像:

sort(a.begin(), a.end());
for(size_t i = 0; i < b.size(); i++)
{
 if (binary_search(a.begin(), a.end(), b[i]))
 {
     swap(b[i], b[b.size()-1]); //remove current element by swapping with last
     b.pop_back();     // and removing new last by shrinking
 }
}

可以更优雅地完成吗? 优雅是主观的,所以在这个Q的范围内被定义为更清晰的代码(理想情况下来自STL算法,但我认为它无法完成),但没有内存分配,也没有增加alg复杂性。

5 个答案:

答案 0 :(得分:9)

这个在O(N + M)中进行,假设两个数组都已排序。

  auto ib = std::begin(two);
  auto iter = std::remove_if (
       std::begin(one), std::end(one),
       [&ib](int x) -> bool {
                       while  (ib != std::end(two) && *ib < x) ++ib;
                       return (ib != std::end(two) && *ib == x);
                     });

答案 1 :(得分:6)

排序b,以便您可以二进制搜索它,以减少时间复杂度。然后使用erase-remove惯用法,以便丢弃a中包含的b中的所有元素:

sort( begin(b), end(b) );
a.erase( remove_if( begin(a),end(a),
    [&](auto x){return binary_search(begin(b),end(b),x);}), end(a) );

当然,您仍然可以通过删除sort()并将binary_search()替换为find()来简化时间复杂度以简化并减少代码:

a.erase( remove_if( begin(a),end(a),
    [&](auto x){return find(begin(b),end(b),x)!=end(b);}), end(a) );

这是品味问题。在这两种情况下,您都不需要堆分配。顺便说一下,我使用的是lambda自动参数,它们是C ++ 14。一些编译器已经实现了诸如clang之类的功能。如果你没有这样的编译器,但只有C ++ 11,那么用容器的元素类型替换auto

顺便说一下,这段代码没有提到任何类型!您可以编写模板函数,因此它适用于所有类型。第一个变体需要b的随机访问迭代,而第二个代码不需要。{{1}}。

答案 2 :(得分:3)

想到的一个解决方案是remove_ifbinary_search的结合。它与手动循环解决方案实际上相同,但由于它使用了更多STL功能,因此可能更“优雅”。

sort(begin(b), end(b));
auto iter = remove_if(begin(a), end(a), 
                      [](auto x) { 
                          return binary_search(begin(b), end(b), x); 
                      });
// Now [begin(a), iter) defines a new range, and you can erase them however
// you see fit, based on the type of a.

答案 3 :(得分:1)

目前的代码非常清楚,因为任何程序员都应该明白发生了什么。

目前的表现是O(a.size() * b.size()),根据实际尺寸,这可能会非常糟糕。

更简洁和类似STL的描述方法是使用remove_if和谓词,该谓词告诉你a中的值是否为。

b.erase(std::remove_if(b.begin(), b.end(), [](const auto&x) {
  return std::find(a.begin(), a.end(), x) != a.end();
}), b.end());

(未经过测试,因此我可能会出现语法错误。)我使用了lambda,但如果您不使用C ++ 11编译器,则可以创建一个仿函数。

请注意,原始代码只删除了ba中的一个值实例。我的解决方案将从b中删除此类值的所有实例。

请注意,find操作会一次又一次地发生,因此最好在较小的矢量上执行此操作以获得更好的参考局部性。

答案 4 :(得分:0)

经过一段时间的思考,我想到了这一点 (注意:回答我自己的问题,我不是声称这比A提供的要好):

vector<int64_t> a{3,2,7,5,11,13}, b{2,3,13,5};
set<int64_t> bs(b.begin(), b.end());
for (const auto& num: bs)
    cout << num << " ";
cout  << endl;
for (const auto& num: a)
    bs.erase(num);
vector<int64_t> result(bs.begin(), bs.end());
for (const auto& num: result)
    cout << num << " ";