如何返回包含不在集合中的元素的向量副本?

时间:2010-06-22 14:39:49

标签: c++ stl c++11

假设我有以下两种数据结构:

std::vector<int> all_items;
std::set<int> bad_items;

all_items向量包含所有已知项目,bad_items向量包含不良项目列表。这两个数据结构完全相互独立地填充。

编写一个返回std::vector<int>的方法的正确方法是什么?包含all_items中不包含bad_items的所有元素?

目前,我有一个笨重的解决方案,我认为可以更简洁地完成。我对STL功能适配器的理解不足。因此问题。我目前的解决方案是:

struct is_item_bad {
    std::set<int> const* bad_items;
    bool operator() (int const i) const {
        return bad_items.count(i) > 0;
    }
};

std::vector<int> items() const {
    is_item_bad iib = { &bad_items; };
    std::vector<int> good_items(all_items.size());
    std::remove_copy_if(all_items.begin(),  all_items.end(), 
                        good_items.begin(), is_item_bad);
    return good_items; 
}

假设all_itemsbad_itemsis_item_baditems()都是某些包含类的一部分。有没有办法把它们写成items() getter:

  • 方法中不需要临时变量吗?
  • 它不需要自定义仿函数struct is_item_bad

我原本希望在count上使用std::set方法作为函子,但我无法用正确的方式表达w remove_copy_if算法

编辑:修复了items()中的逻辑错误。实际的代码没有问题,这是一个转录错误。

编辑:我接受了一个不使用std::set_difference的解决方案,因为它更通用,即使std::vector未排序也会有效。我选择在我的代码中使用C ++ 0x lambda表达式语法。我的最终items()方法如下所示:

std::vector<int> items() const {
    std::vector<int> good_items;
    good_items.reserve(all_items.size());
    std::remove_copy_if(all_items.begin(), all_items.end(),
                        std::back_inserter(good_items),
                        [&bad_items] (int const i) {
                            return bad_items.count(i) == 1;
                        });
}

在大约800万个项目的向量上,上述方法在3.1s中运行。我替补标记了std::set_difference方法,它在大约2.1s内运行。感谢所有提供了很好答案的人。

4 个答案:

答案 0 :(得分:8)

正如jeffamaphone建议的那样,如果你可以对任何输入向量进行排序,你可以使用std::set_difference,这是有效的,代码更少:

#include <algorithm>
#include <set>
#include <vector>

std::vector<int> 
get_good_items( std::vector<int> const & all_items,
                std::set<int> const & bad_items )
{
    std::vector<int> good_items;

    // Assumes all_items is sorted.
    std::set_difference( all_items.begin(),
                         all_items.end(),
                         bad_items.begin(),
                         bad_items.end(),
                         std::back_inserter( good_items ) );

    return good_items;
}

答案 1 :(得分:3)

由于你的函数将返回一个向量,你必须在任何情况下创建一个新的向量(即复制元素)。在这种情况下,std::remove_copy_if很好,但您应该正确使用它:

#include <iostream>
#include <vector>
#include <set>
#include <iterator>
#include <algorithm>
#include <functional>
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
        std::vector<int> result;
        remove_copy_if(all.begin(), all.end(), back_inserter(result),
                  [&bad](int i){return bad.count(i)==1;});
        return result;
}

int main()
{
        std::vector<int> all_items = {4,5,2,3,4,8,7,56,4,2,2,2,3};
        std::set<int> bad_items = {2,8,4};
        std::vector<int> filtered_items = filter(all_items, bad_items);
        copy(filtered_items.begin(), filtered_items.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << std::endl;
}

要在C ++ 98中执行此操作,我想您可以使用mem_fun_refbind1st将set :: count转换为内嵌函数,但是存在问题(由此产生的问题)在C ++ 0x中对bind1st的弃用意味着取决于你的编译器,你最终可能会使用std :: tr1 :: bind:

remove_copy_if(all.begin(), all.end(), back_inserter(result),
     bind(&std::set<int>::count, bad, std::tr1::placeholders::_1)); // or std::placeholders in C++0x

在任何情况下,我认为显式函数对象更具可读性:

struct IsMemberOf {
        const std::set<int>& bad;
        IsMemberOf(const std::set<int>& b) : bad(b) {}
        bool operator()(int i) const { return bad.count(i)==1;}
};
std::vector<int> filter(const std::vector<int>& all, const std::set<int>& bad)
{
        std::vector<int> result;
        remove_copy_if(all.begin(), all.end(), back_inserter(result), IsMemberOf(bad));
        return result;
}

答案 2 :(得分:2)

冒着出现过时的风险:


std::set<int> badItems;
std::vector<int> items;
std::vector<int> goodItems;
for ( std::vector<int>::iterator iter = items.begin();
      iter != items.end();
      ++iter)
{
   int& item = *iter;
   if ( badItems.find(item) == badItems.end() )
   {
      goodItems.push_back(item);
   }
}

答案 3 :(得分:1)

std::remove_copy_if返回目标集合的迭代器。在这种情况下,它将返回good_items.end()(或类似的东西)。 good_items在方法结束时超出范围,因此这会导致一些内存错误。您应该返回good_items或通过引用传递new vector<int>,然后clearresize并填充它。这将摆脱临时变量。

我相信你必须定义自定义函子,因为该方法取决于你没有得到hackey AFAIK时无法指定的对象bad_items