n个向量的交集

时间:2015-03-28 16:34:41

标签: c++11 set-intersection

我是编程新手,我最近遇到了一个问题,即找到已经排序的n个向量(int向量)的交集。我提出的方法具有O(n ^ 2)的复杂性,我使用的是std :: set_intersect函数。

我想出的方法是通过两个向量:第一个向量对应于我拥有的第一个向量,第二个向量将是第二个向量。我在两个上调用set intersection并覆盖第一个向量,然后在第二个向量上使用向量清除函数。然后我将下一个向量覆盖到第二个向量,并重复该过程,最终返回第一个向量。

我相信有一种更有效的方法可以解决这个问题,但目前我无法想到更有效率的方式。对此问题的任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

幸运的是,我认为可以采取更严格的约束 算法的复杂性。

大小为n1和n2的输入集上std::set_intersection的复杂性是 O(n1 + n2)。 您可以将原始矢量与单消除相交 比赛风格,即在第一轮与第一轮和第二轮交叉 向量,第3和第4,第5和第6,等等;在...上 第二轮与第一和第二交叉点相交,第3和第4交叉点, 等等;重复,直到最后一轮产生一个交叉点。 每轮存活的所有向量的大小总和不超过 这一轮开始时矢量大小的一半, 所以这个算法一共需要O(N)时间(也是O(N)空间) 其中N是输入中所有原始矢量的大小之和。 (它是O(N),因为N + N / 2 + N / 4 + ...... <2N。)

因此,给定一个由已经排序的向量组成的输入, 算法的复杂性是O(N)。

您的算法以非常不同的顺序合并矢量, 虽然我并非100%确定它也是O(N),但我强烈怀疑它是。(/ p>


修改 关于如何实际实施&#34;锦标赛&#34; C ++中的算法, 这取决于你想要努力优化这个, 而且有点关于你输入的性质。

最简单的方法是制作一个新的载体列表;从旧列表中取两个向量,将一个向量推入新列表,将两个旧向量合并到新向量上,销毁旧向量,希望库有效地管理内存。

如果要减少新向量的分配,则重新使用向量 (正如你已经想过的那样)可能有所帮助。如果输入数据结构是 例如,您可以通过将一个空向量推到此列表的前面来开始std::list<std::vector<int> >。制作三个迭代器,一个用于新向量,一个用于列表中原始的前两个向量。 取最后两个迭代器的向量的交集, 将结果写入第一个迭代器,然后清除向量 最后两个迭代器。将最后两个迭代器向前移动两个位置, 将第一个迭代器向前移动一个位置。重复。如果你到达一个州 最后两个迭代器中的一个已达到end()但另一个没有, 擦除第一个迭代器和另一个迭代器之间的所有列表元素。 现在你又有了一个向量列表,只要有,就可以重复 列表中有多个向量。

如果输入为std::vector<std::vector<int> >,则推送元素 在列表的前面是相对昂贵的,所以你可能想要一个 稍微复杂的算法。有很多选择,不是真的 我能想到的明显的赢家。

答案 1 :(得分:1)

这是另一项分析,显示您的算法已经是线性的。

假设您有一些向量集合,算法会从集合中重复选择两个向量,并用它们的交集替换它们,直到剩下一个向量。您的方法符合此描述。我认为任何这样的算法都会在set_intersection的所有执行中花费总计线性时间。

假设set_intersectionA * (x + y)x大小的向量进行最多y次操作。

K为集合中所有向量的长度之和。它以输入(n)的大小开始,不能低于零,因此最多可以更改n

每当大小(xy)的向量组合K的值减少至少(x + y)/2时,结果必须比任何一个都短输入。如果我们对所有调用进行总结,那么我们会得到sum { (x + y)/2 } <= n,因为K的变化不能超过n

由此我们可以推导出sum { A * (x + y) } <= 2 * A * n = O(n)。这里的左侧是在set_intersection中花费的总时间。

使用不太正式的语言 - 要花费x + y时间set_intersection,您需要从集合中删除至少(x + y)/2个元素,因此花费的时间超过执行set_intersection的线性时间会让你的元素耗尽。