【发布时间】:2010-10-28 02:59:58
【问题描述】:
如何在 c++ 中对 tr1::unordered_set 类型的集合进行交集和并集?我找不到太多关于它的参考。
任何参考和代码都将受到高度赞赏。非常感谢。
更新:我只是猜想 tr1::unordered_set 应该提供交集、并集、差集的功能。因为这是集合的基本操作。 当然我可以自己写一个函数,但我只是想知道是否有来自 tr1 的内置函数。 非常感谢。
【问题讨论】:
如何在 c++ 中对 tr1::unordered_set 类型的集合进行交集和并集?我找不到太多关于它的参考。
任何参考和代码都将受到高度赞赏。非常感谢。
更新:我只是猜想 tr1::unordered_set 应该提供交集、并集、差集的功能。因为这是集合的基本操作。 当然我可以自己写一个函数,但我只是想知道是否有来自 tr1 的内置函数。 非常感谢。
【问题讨论】:
我看到set_intersection() 等人。 algorithm 标头中的内容不起作用,因为它们明确要求对输入进行排序——猜想你已经排除了它们。
在我看来,遍历哈希 A 并查找哈希 B 中的每个元素的“幼稚”方法实际上应该为您提供接近最佳的性能,因为哈希 B 中的连续查找将转到同一个哈希桶 (假设两个哈希都使用相同的哈希函数)。即使这些存储桶几乎可以肯定是作为链表实现的,这也应该会给您提供不错的内存局部性。
这是unordered_set_difference() 的一些代码,您可以对其进行调整以制作集合并集和集合差异的版本:
template <typename InIt1, typename InIt2, typename OutIt>
OutIt unordered_set_intersection(InIt1 b1, InIt1 e1, InIt2 b2, InIt2 e2, OutIt out) {
while (!(b1 == e1)) {
if (!(std::find(b2, e2, *b1) == e2)) {
*out = *b1;
++out;
}
++b1;
}
return out;
}
假设您有两个 unordered_sets、x 和 y,您可以将它们的交集放在 z 中:
unordered_set_intersection(
x.begin(), x.end(),
y.begin(), y.end(),
inserter(z, z.begin())
);
与bdonlan's answer 不同,这实际上适用于任何键类型和容器类型的任何组合(尽管如果源容器已排序,使用set_intersection() 当然会更快)。
注意:如果存储桶占用率很高,将每个哈希复制到 vector 中,对它们进行排序并在那里进行set_intersection() 可能会更快,因为在包含 n 个元素的存储桶中搜索是 O(n)。
【讨论】:
set 的迭代器一起工作吗? find 不会简单地遍历第二组中的每个元素,而我们希望它使用散列来循环吗?该函数不应该简单地引用 set 对象,然后使用.count 方法吗?
intelligently_find<T>() 来引用容器(而不是迭代器对),并为允许的容器提供重载快速查找,否则让它退回到std::find()。
std::copy( std::begin(uset2), std::end(uset2), std::inserter( uset1, std::end(uset1) ) );
没什么大不了的 - 对于相交,只需遍历一个元素的每个元素并确保它在另一个元素中。对于联合,添加两个输入集中的所有项目。
例如:
void us_isect(std::tr1::unordered_set<int> &out,
const std::tr1::unordered_set<int> &in1,
const std::tr1::unordered_set<int> &in2)
{
out.clear();
if (in2.size() < in1.size()) {
us_isect(out, in2, in1);
return;
}
for (std::tr1::unordered_set<int>::const_iterator it = in1.begin(); it != in1.end(); it++)
{
if (in2.find(*it) != in2.end())
out.insert(*it);
}
}
void us_union(std::tr1::unordered_set<int> &out,
const std::tr1::unordered_set<int> &in1,
const std::tr1::unordered_set<int> &in2)
{
out.clear();
out.insert(in1.begin(), in1.end());
out.insert(in2.begin(), in2.end());
}
【讨论】:
us_union 中,执行out = in1; 应该比从迭代器范围中清除和插入更有效,因为不需要在插入时测试重复项。在us_isect 中,out.clear() 可以跟踪检查较小容器的条件,因为不需要清除它两次。我会简单地使用in2.count(*it) 而不是使用in2.find(*it) != in2.end()
基于上一个答案:
C++11版本,如果集合支持快速查找功能find()
(返回值被有效移动)
/** Intersection and union function for unordered containers which support a fast lookup function find()
* Return values are moved by move-semantics, for c++11/c++14 this is efficient, otherwise it results in a copy
*/
namespace unorderedHelpers {
template<typename UnorderedIn1, typename UnorderedIn2,
typename UnorderedOut = UnorderedIn1>
UnorderedOut makeIntersection(const UnorderedIn1 &in1, const UnorderedIn2 &in2)
{
if (in2.size() < in1.size()) {
return makeIntersection<UnorderedIn2,UnorderedIn1,UnorderedOut>(in2, in1);
}
UnorderedOut out;
auto e = in2.end();
for(auto & v : in1)
{
if (in2.find(v) != e){
out.insert(v);
}
}
return out;
}
template<typename UnorderedIn1, typename UnorderedIn2,
typename UnorderedOut = UnorderedIn1>
UnorderedOut makeUnion(const UnorderedIn1 &in1, const UnorderedIn2 &in2)
{
UnorderedOut out;
out.insert(in1.begin(), in1.end());
out.insert(in2.begin(), in2.end());
return out;
}
}
【讨论】: