从一组集合中查找集合子集的最佳方法

时间:2012-02-15 17:43:52

标签: c++ algorithm set subset

首先,抱歉这个含糊不清的标题。

假设我有以下几组:

第1组

s1 = ( x1, y1 )
s2 = ( x2 )

第2组

m1 = ( x1, y1, y2 )
m2 = ( x1 )
m3 = ( x1 , x2 )

对于Group 1中的每个集合 - 调用集合s,我需要在Group 2中找到这些集合 - 将其称为m - 这样{{1} }}是m的一个子集。

因此,对于我的例子,答案是:

s

现在,我将值存储在s1 -> m2 s2 -> nothing 中,但如果需要,我可以更改它。此外,集合可能会变大,因此算法需要高效。现在我有一种蛮力的方法,我并不完全满意。

有什么建议吗?

4 个答案:

答案 0 :(得分:1)

第一步是根据基数(即大小)对第1组进行排序。然后算法大概是这样的:

foreach std::set M in "Group 2" {
  foreach std::set S in "Group 1" and S.size()>=M.size() {  // replace with binary search
     if ( std::includes(S.begin(),S.end(),M.begin(),M.end()) )
       { /* M is a subset of S */ }
    }
  }
}

这应该具有时间复杂度~O(MSR),其中M是“组2”中的集合数,S是“组1”中的集合数,并且R是“组#”中最大集合的大小1" 。

编辑:我刚想到使用S.find()而不是调用std::includes()(顺序迭代)可能更有效但我认为这只会是如果M.size()远小于S.size() - O(M + S)vs O(MlogS),则返回true。

答案 1 :(得分:0)

您并不具体说明您的方法是如何蛮力的。只要您在std :: namespace中使用set query函数,它们就可能尽可能高效。 例如,测试set_intersection(s1.begin(),s2.end(),m1.begin(),m1.end())是否等于m1。

你可能比这更有效率,因为你不需要匹配元素的副本,只是知道它们都出现了。这可以通过复制set_intersection的代码但更改实现来简单地计算匹配元素的数量而不是将它们复制出来来完成。然后,如果计数与m的大小相同,那么你就有了匹配。

至于容器,我经常喜欢在大型集合的集合上使用排序的双端队列。内存在堆上分布较少,这有助于缓存。它还避免了底层树的开销。当容器被创建一次但被多次搜索时,这尤其有用。

答案 2 :(得分:0)

您的套装是经常修改的还是只读/大多数?

  • 如果经常修改,std::set是修改和排序性能之间的良好平衡。
  • 如果是只读或大部分是读取,您可以使用已排序的std::vector。排序很昂贵,但实际上比在std::set中构建整棵树便宜,所以如果你做的很少,性能会更好。

完成已排序的容器(无论是“自动排序”std::set还是手动排序std::vector)后,您可以使用std::includes测试子集。顺便说一句,如果您需要找到正确的子集,您可以在之后比较元素计数。

答案 3 :(得分:0)

你可以尝试这样的事情。 步骤进行:

  • 创建一个包含两个组中所有对象的数组
  • 转换位数组中的每个s和m,其中如果集合包含object(i),则array(i)= 1,否则为0
  • m(k)是s(j)的子集,如果m(k)AND s(j)= m(k)