集合的有效交集?

时间:2014-01-07 19:15:38

标签: c++ set std

我想知道最有效的方法是什么。

我有点从两个地方收集。

我只对这两个地方共有的要点感兴趣。

我的计划是有3 std::set<Point>。首先,我将从区域A添加点到集合A,然后从B点到集合B,让集合C成为两个集合的交集。

但是,我想知道是否有更好的方法可以做到这一点,可能涉及更少的设置?

由于

7 个答案:

答案 0 :(得分:4)

你的问题很常见,甚至有一种(以明显的方式命名)standard algorithm set_intersection()供你使用。

答案 1 :(得分:1)

你可以取消集合B:首先从A收集点到集合A,然后从B收集点,但只有当它们也存在于A中时才将它们放入C.

如果A组和B组的大小不同(且可预测),那么明显的选择就是消除较大的大小。

答案 2 :(得分:1)

天真的基于集合的方法(构建两个集合,然后生成交集),具有以下步骤:

  1. 从源代码构建std::set A 1.说源1有N个点,这是:
    • O(N log N)时间,O(N)空间
  2. 从源2构建std::set B。假设这有M分,给出:
    • O(M log M)时间,O(M)空间
  3. std::set_intersection
    • O(M + N)时间和空间
  4. 略有改进的基于集合的方法是:

    1. 从第一个来源构建std::set A(与上面相同的复杂性)
    2. 对于第二个来源中的每个点,如果(并且仅当)它在集合A中,则将其添加到结果中

      • O(M log N)时间,线性空间
      • 所以,你避免分配O(M)额外空间,以及额外的O(M + N)交叉步骤。

      您可以使用std::set执行此操作,如下所示:

      if (a.find(point) != a.end())
          result.insert(point);
      
    3. 请注意,如果您知道哪个点源将提供更少的点,则应使用该源构建集A以获得最佳性能。如果您的来源按排序顺序提供点数,您可以完全避免这些集合并节省更多空间和空间。时间。

答案 3 :(得分:0)

您可以map<Point, int>将每个点映射到它出现的次数,即1或2.然后删除仅出现一次的所有点。

答案 4 :(得分:0)

您可以使用map<Point,int>存储发生1或2次的点数。在这种情况下,您还必须重载<运算符。

答案 5 :(得分:0)

我认为使用三组和std :: set_intersection的方法是最合乎逻辑和最有效的。虽然如果你真的想拥有更少的集合,你可以迭代集合B并删除不在A中的点然后迭代集合A并删除不在B中的点。这应该这样做。

答案 6 :(得分:0)

如果您在点上定义词典operator<,即(如果需要,推断&gt; 2维)

bool operator<(Point const &a, Point const &b)
{
    if (a.x != b.x) return a.x < b.x;
    return a.y < b.y;
}

然后std::set可能具有足够好的性能来有效地计算交叉点。假设您的两个组已经在数组中,或者在运行中生成,则需要2组:

A = { all points from A }
C = { }
for all points in B:
    if point is in A:
        add point to C

如果您使用浮点坐标,那么您可能需要允许浮点错误并搜索A 中<{1>} 附近的所有点{{1} }。然后,您需要一个空间分区结构,例如k-d treequadtree/octree。这将涉及引入一个新的库或编写一个相当复杂的数据结构,所以如果可能的话我会避免它。