【问题标题】:algorithm to remove elements in the intersection of two sets删除两个集合交集中元素的算法
【发布时间】:2012-05-15 14:53:00
【问题描述】:

我有一个 Visual Studio 2008 C++03 应用程序,其中有两个标准容器。我想从一个容器中删除另一个容器中存在的所有项目(集合的交集)。

类似这样的:

std::vector< int > items = /* 1, 2, 3, 4, 5, 6, 7 */;
std::set< int > items_to_remove = /* 2, 4, 5*/;

std::some_algorithm( items.begin, items.end(), items_to_remove.begin(), items_to_remove.end() );

assert( items == /* 1, 3, 6, 7 */ )

是否有现有的算法或模式可以做到这一点,还是我需要自己推出?

谢谢

【问题讨论】:

  • 投反对票有什么原因吗?有什么方法可以改进我表达问题的方式吗?或者这只是一个开车去repping?

标签: c++ algorithm


【解决方案1】:

尝试:

items.erase(
    std::remove_if(
        items.begin(), items.end()
      , std::bind1st(
            std::mem_fun( &std::set< int >::count )
          , items_to_remove
        )
    )
  , items.end()
);

std::remove(_if) 实际上并没有删除任何东西,因为它适用于迭代器而不是容器。它所做的是将要在范围末尾删除的元素重新排序,并将迭代器返回到容器的 new end。然后调用erase 从容器中移除所有超过新端的元素。

更新:如果我没记错的话,绑定到标准库组件的成员函数不是标准 C++,因为允许实现向函数添加默认参数。通过创建自己的函数或函数对象谓词来检查元素是否包含在要删除的项目集中,您会更安全。

【讨论】:

  • 或者在 C++11 中,您可以使用 lambda:[&amp;](int i) { return items_to_remove.count(i); }
  • @Steve Jessop:在 C++11 中,但 OP 正在使用 C++03
  • 这就是为什么它不是一个答案:-)
【解决方案2】:

就个人而言,我更喜欢为此创建小型助手(我大量重复使用)。

template <typename Container>
class InPredicate {
public:
    InPredicate(Container const& c): _c(c) {}

    template <typename U>
    bool operator()(U const& u) {
        return std::find(_c.begin(), _c.end(), u) != _c.end();
    }

private:
    Container const& _c;
};

// Typical builder for automatic type deduction
template <typename Container>
InPredicate<Container> in(Container const& c) {
    return InPredicate<Container>(c);
}

这也有助于拥有真正的erase_if 算法

template <typename Container, typename Predicate>
void erase_if(Container& c, Predicate p) {
    c.erase(std::remove_if(c.begin(), c.end(), p), c.end());
}

然后:

erase_if(items, in(items_to_remove));

这很可读:)

【讨论】:

  • 您可能希望将InPredicate 部分专门化为set(在此示例中)和map,以降低速度。
  • @SteveJessop:可能,但这是一种优化:)
  • 是的。想想看,你也可以重载erase_if 来处理非序列。
【解决方案3】:

另一种解决方案:

有标准提供的算法 set_difference 可用于此。 但它需要额外的容器来保存结果。我个人更喜欢就地进行。

std::vector< int > items;
//say items = [1,2,3,4,5,6,7,8,9]

std::set<int>items_to_remove;
//say items_to_remove = <2,4,5>

std::vector<int>result(items.size()); //as this algorithm uses output 
                           //iterator not inserter iterator for result.

std::vector<int>::iterator new_end = std::set_difference(items.begin(), 
 items.end(),items_to_remove.begin(),items_to_remove.end(),result.begin());

result.erase(new_end,result.end()); // to erase unwanted elements at the 
                                    // end.

【讨论】:

  • 您可以使用back_insert_iterator 作为set_difference() 的最后一个参数。它会自动将元素推回result
【解决方案4】:

为此,您可以将std::erasestd::remove 结合使用。有一个名为 Erase - Remove idiom 的 C++ 习语可以帮助您完成此操作。

【讨论】:

    【解决方案5】:

    假设您有两个集合,AB,并且您想从 B 中删除 (A,B) 的交集 I,这样 I = A^B,您的最终结果将是: A(保持原样)
    B' = B-I

    完整理论:
    http://math.comsci.us/sets/difference.html

    这很简单。

    1. 创建并填充AB
    2. 创建第三个中间向量I
    3. B的内容复制到I
    4. 对于包含j元素的A中的每个元素a_j,在I中搜索元素a_j;如果在I 中找到该元素,请将其删除

    最后,删除单个元素的代码可以在这里找到:
    How do I remove an item from a stl vector with a certain value?

    搜索商品的代码在这里:
    How to find if an item is present in a std::vector?

    祝你好运!

    【讨论】:

      【解决方案6】:

      这是一个更“动手”的就地方法,它不需要花哨的功能,也不需要对向量进行排序:

      #include <vector>
      
      template <class TYPE>
      void remove_intersection(std::vector<TYPE> &items, const std::vector<TYPE> &items_to_remove)
      {
          for (int i = 0; i < (int)items_to_remove.size(); i++) {
              for (int j = 0; j < (int)items.size(); j++) {
                  if (items_to_remove[i] == items[j]) {
                      items.erase(items.begin() + j);
                      j--;//Roll back the iterator to prevent skipping over
                  }
              }
          }
      }
      

      如果您知道每个集合中的多重性为 1(不是 multiset),那么您实际上可以将 j--; 行替换为 break; 以获得更好的性能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多