我是编程新手,我最近遇到了一个问题,即找到已经排序的n个向量(int向量)的交集。我提出的方法具有O(n ^ 2)的复杂性,我使用的是std :: set_intersect函数。
我想出的方法是通过两个向量:第一个向量对应于我拥有的第一个向量,第二个向量将是第二个向量。我在两个上调用set intersection并覆盖第一个向量,然后在第二个向量上使用向量清除函数。然后我将下一个向量覆盖到第二个向量,并重复该过程,最终返回第一个向量。
我相信有一种更有效的方法可以解决这个问题,但目前我无法想到更有效率的方式。对此问题的任何帮助将不胜感激。
答案 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_intersection
对A * (x + y)
和x
大小的向量进行最多y
次操作。
让K
为集合中所有向量的长度之和。它以输入(n
)的大小开始,不能低于零,因此最多可以更改n
。
每当大小(x
,y
)的向量组合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
的线性时间会让你的元素耗尽。