找到集合并集的最快方法

The fastest way to find union of sets

本文关键字:方法 集合 合并      更新时间:2023-10-16

我有一组像 int 一样的 int 对 set<pair<int,int> > x1, x2, ... xn(n 可以介于 2 和 20 之间)。找到这些集合并集的最快方法是什么?

抱歉,如果我一开始没有说清楚,我的意思是性能快,内存分配不是问题。

假设结果也需要是一个集合,那么您别无选择,只能将每个x_i的每个元素插入到该结果集中。所以显而易见的实现是:

set<pair<int,int>> x(x1);
x.insert(x2.begin(), x2.end());
// etc

剩下的问题是这是否可以在速度上被击败。

单元素insert采用position提示,如果正确,则会加快插入速度。所以结果可能是这样的事情比x.insert(x2.begin(), x2.end());快:

auto pos = x.begin()
for (auto it = x2.begin(); it != x2.end(); ++it) {
    pos = x.insert(pos, *it);
}

不过,这取决于数据:该位置可能准确,也可能不准确。您可以通过在开始之前将所有元素整理好来确保它,为此最好的工具可能是 set_union .这最好命名为merge_and_dedupe_sorted_ranges,因为它所做的与std::set没有特别的关系。你可以set_union到中间向量中,或者变成这样的集合:

set<pair<int,int>> x;
set_union(x1.begin(), x1.end(), x2.begin(), x2.end(), inserter(x, x.end());

我对使用 set_union 的担忧是,为了获得以递增顺序将元素添加到集合中的好处,每次调用它时都需要创建一个新的空容器(因为如果它不为空,那么添加的元素需要与其中已有的值交错)。这些容器的开销可能高于以任意顺序插入到集合中的开销:您必须对其进行测试。

不幸的是,我相信您仅限于线性O(N)解决方案,因为所有并集都是两个集合中元素的组合。

template<typename S>
S union_sets(const S& s1, const S& s2)
{
     S result = s1;
     result.insert(s2.cbegin(), s2.cend());
     return result;
}

首先找到最小集合的并集。也就是说,按集合长度对集合进行排序,计算两个最小集合的并集,删除这些集合,根据其大小将并集插入到集合列表中。

如果你测量了两个集合的相似程度,那么你最好先找到最相似集合的并集。这是更倾向于尽早消除重复项的工会操作。

编辑:对于两个集合之间的每个并集操作 - 将较小的集合合并到较大的集合中。

我假设快速你的意思是快速实现

然后: 标准::set_union (*)

两组示例:

#include <set>
#include <algorithm>
#include <iterator>
using namespace std;
int main () {
    set<pair<int,int> > a, b, uni;
    set_union (a.begin(), a.end(),
               b.begin(), b.end(),
               inserter(uni, uni.begin()));
}

对于 n 个集合,手写它可能是最易于维护的解决方案:

#include <set>
#include <vector>
using namespace std;
int main () {
    vector<set<pair<int,int>>> sets;
    set<pair<int,int>> uni;
    for (const auto &s : sets)
        for (const auto &elem : s)
            uni.insert (elem);
}

尽管一般来说,人们应该更喜欢标准算法并从其质量实现中获利。

如果您所说的快速是指性能,我们无能为力,因为我们没有要求。对于不同的情况,不同的方法可能会产生不同的结果。


(*) 注意:该网站有时因为与标准相比不是 100% 准确而皱眉

尝试标头算法中的set_union。

为了节省内存分配并改善局部性,最好使用单个vector<T>作为工作内存。

构造一个vector<T>并保留所有 s 中的元素总数(计算重复项)。 然后,从空范围[v.begin(), v.begin())开始,通过附加每个集合的内容,合并和统一,将其扩展到类似集合(唯一,排序)的范围:

vector<T> v;
v.reserve(<total size>);
for (set<T> &s: sets) {
    auto middle = v.insert(v.end(), s.begin(), s.end());
    inplace_merge(v.begin(), middle, v.end());
    v.erase(v.unique(v.begin(), v.end()), v.end());
}
你可以

使用 std::set_union递归或简单地将所有集合插入到结果集中(重复项由集合消除)。如果项目数量非常少,您可以尝试将其全部插入到向量中,对其进行排序并在向量上使用 std::unique。