【问题标题】:tr1::unordered_set union and intersectiontr1::unordered_set 联合和交集
【发布时间】:2010-10-28 02:59:58
【问题描述】:

如何在 c++ 中对 tr1::unordered_set 类型的集合进行交集和并集?我找不到太多关于它的参考。

任何参考和代码都将受到高度赞赏。非常感谢。

更新:我只是猜想 tr1::unordered_set 应该提供交集、并集、差集的功能。因为这是集合的基本操作。 当然我可以自己写一个函数,但我只是想知道是否有来自 tr1 的内置函数。 非常感谢。

【问题讨论】:

    标签: c++ set tr1


    【解决方案1】:

    我看到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、xy,您可以将它们的交集放在 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)。

    【讨论】:

    • 我有点担心。我们确定 std::find 可以很好地与 set 的迭代器一起工作吗? find 不会简单地遍历第二组中的每个元素,而我们希望它使用散列来循环吗?该函数不应该简单地引用 set 对象,然后使用.count 方法吗?
    • ... 根据您的建议,最好编写一个免费的模板函数intelligently_find&lt;T&gt;() 来引用容器(而不是迭代器对),并为允许的容器提供重载快速查找,否则让它退回到std::find()
    • -1 这个答案:我做了一些实验来确认 std :: find 很慢,因此我赞成@bdonlan的答案。 ideone.com/Lr64p(感谢@j_random_hacker)
    • 这里是 unordered_set 的 set_union:std::copy( std::begin(uset2), std::end(uset2), std::inserter( uset1, std::end(uset1) ) );
    • "如果存储桶占用率很高" 那么您的性能问题不仅仅是联合/交叉点;也许你的哈希函数不好?
    【解决方案2】:

    没什么大不了的 - 对于相交,只需遍历一个元素的每个元素并确保它在另一个元素中。对于联合,添加两个输入集中的所有项目。

    例如:

    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()
    【解决方案3】:

    基于上一个答案: 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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 2017-12-18
      • 2021-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多