如何返回包含不在集合中的元素的向量的副本?
Posted
技术标签:
【中文标题】如何返回包含不在集合中的元素的向量的副本?【英文标题】:How can I return a copy of a vector containing elements not in a set? 【发布时间】:2010-06-22 14:39:49 【问题描述】:假设我有以下两种数据结构:
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_items
、bad_items
、is_item_bad
和items()
都是某些包含类的一部分。有没有办法写items()
getter 这样:
struct is_item_bad
?
我曾希望只使用std::set
上的count
方法作为函子,但我无法用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.1 秒内运行。我在工作台上标记了std::set_difference
方法,它运行了大约 2.1 秒。感谢所有提供出色答案的人。
【问题讨论】:
也许你可以使用std::set_difference? cplusplus.com/reference/algorithm/set_difference 如果您没有重复的对象,您可以为此使用两个集合和集合操作。但我猜你需要一个向量。 【参考方案1】:正如 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;
【讨论】:
@dribeas:我很确定这就是他所说的意思:“......如果你可以对任何输入向量进行排序......” 向量是有序的。数据以有序的方式到达我这里,然后我将其插入到向量中。我没有为此使用集合,因为我不想为已经自然排序的数据添加额外的集合比较器开销。是不是搞错了? 不,Lrm,这很好,只要向量中的顺序与您使用集合而不是向量时它们将的顺序相同。 【参考方案2】:由于您的函数将返回一个向量,因此无论如何您都必须创建一个新向量(即复制元素)。在这种情况下,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_ref
和 bind1st
将 set::count 转为内联函子,但这样做存在问题(导致 bind1st 被弃用在 C++0x 中)这意味着取决于您的编译器,您最终可能会使用 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;
【讨论】:
+1... 很明显,因为这和我自己写的差不多,而且更完整! Cubbi,感谢您的详细回答。我已经修复了逻辑错误——它是一个转录错误,并且不存在于原始代码中。我认为您对可读性的看法是正确的。你能解释一下你的第一个解决方案吗?我不熟悉仿函数 [&bad](int i)... 的语法是创建某种 lambda 表达式吗? @lrm:是的,第一个示例使用 C++0x lambda 表达式。它构建了一个函数对象,通过引用捕获“坏”并公开bool operator()(int i) const
——就像你最初写的一样,除了使用引用而不是指针。它使用 gcc 4.5+(我想是 MSVC10)编译。第二位使用 C++TR1 通用绑定器(在 c++0x 模式下 gcc 4.4+ 的 std 命名空间中可用,也可作为所有 C++98 编译器的 boost.bind 使用),第三位使用基本 C++98 .【参考方案3】:
冒着显得过时的风险:
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);
【讨论】:
感谢克雷格的回答。我正在尝试使用适当的 STL 算法,因为它是 Meyer 有效 STL 的第 43 项。 @lrm: "适当的 STL 算法" ... STL 算法/等太多了,普通人甚至无法记住其中的一半。我通常会写和克雷格一样的东西。这也是K.I.S.S。合规。然而,当输入向量被排序时,上面的代码是 O(log(n)),而它可以通过简单地并行迭代结构在 O(n) 中完成。【参考方案4】:std::remove_copy_if
返回一个指向目标集合的迭代器。在这种情况下,它将返回good_items.end()
(或类似的东西)。 good_items
在方法结束时超出范围,因此会导致一些内存错误。您应该返回good_items
或通过引用传入new vector<int>
,然后clear
、resize
并填充它。这将摆脱临时变量。
我相信您必须定义自定义仿函数,因为该方法取决于对象 bad_items
,如果没有它获得 hackey AFAIK,您将无法指定该对象。
【讨论】:
谢谢戴夫。这是我的输入错误。实际代码确实返回good_items
。我已经在问题中解决了它。以上是关于如何返回包含不在集合中的元素的向量的副本?的主要内容,如果未能解决你的问题,请参考以下文章
Python如何在一个集合里查找除了我提供的元素以外的元素?
数据结构 已知两个链表A和B分别表示两个集合,其元素递增排列。请设计算法求出两个集合A和B的差集(即仅由在A中出现而不在B中出现的元素所构成的集合),并以同样的形式存储,同时返回该集合的元素个数。(代