合并至少共享 2 个元素的集合的算法
Posted
技术标签:
【中文标题】合并至少共享 2 个元素的集合的算法【英文标题】:Algorithm for merging sets that share at least 2 elements 【发布时间】:2008-11-23 20:27:03 【问题描述】:给定一个集合列表:
S_1:[1、2、3、4] S_2 : [ 3, 4, 5, 6, 7 ]i> S_3:[8,9,10,11] S_4:[1、8、12、13] S_5:[6、7、14、15、16、17]合并至少共享 2 个元素的所有集合的最有效方法是什么?我想这类似于连接组件问题。所以结果是:
[ 1, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17] (S_1 UNION S_2 UNION S_5) [8,9,10,11] [ 1, 8, 12, 13 ](S_4 与 S_1 共享 1,与 S_3 共享 8,但未合并,因为它们仅共享一个元素)朴素的实现是 O(N^2),其中 N 是集合的数量,这对我们来说是行不通的。这需要对数百万组有效。
【问题讨论】:
集合中的值范围是多少? 有整数吗?他们可以在一组内重复吗? 集合中的值为整数,每个集合内不重复 澄清一下:在您的示例中,第一次合并是否应该包括 S_3,因为它与 S_5 共享四个元素? 如果我们有 A:[1,2,3]、B:[1,2,4] 和 C:[3,4,5]。我们是不是先把A和B合并到[1,2,3,4]再和C合并(因为A+B和C有2个共同点,而A和B都没有和C个共同点2? 【参考方案1】:Let there be a list of many Sets named (S)
Perform a pass through all elements of S, to determine the range (LOW .. HIGH).
Create an array of pointer to Set, of dimensions (LOW, HIGH), named (M).
do
Init all elements of M to NULL.
Iterate though S, processing them one Set at a time, named (Si).
Permutate all ordered pairs in Si. (P1, P2) where P1 <= P2.
For each pair examine M(P1, P2)
if M(P1, P2) is NULL
Continue with the next pair.
otherwise
Merge Si, into the Set pointed to by, M(P1, P2).
Remove Si from S, as it has been merged.
Move on to processing Set S(i + 1)
If Si was not merged,
Permutate again through Si
For each pair, make M(P1, P2) point to Si.
while At least one set was merged during the pass.
我的意思是这是关于订单 (2N ln N)。 对此持保留态度。
【讨论】:
这假设一个集合中包含所有介于低位和高位之间的元素,这是不正确的 - 还是我弄错了? 合并 Si 后,您仍然需要置换 Si 中的所有对并将它们添加到 M(指向 M(P1, P2)),然后再转到 Set S(i + 1),对?否则,这看起来不错。 集合 1, 2, 3, 2, 3, 4 和 1, 4 应该发生什么?第一个和第二个被合并,合并后的集合与第三个有两个重复 - 第三个应该合并,还是只有集合的原始内容才重要?我认为这个答案是前者,而不是后者 Paul:是的,这就是我在之前的评论中试图解决的问题。当 2, 3, 4 与 1, 2, 3 合并时,新合并集中的置换对需要添加到 M。 @Claudiu。不,集合不需要是连续的。【参考方案2】:如果您可以对集合中的元素进行排序,则可以考虑在集合上使用 Mergesort。唯一需要的修改是在合并阶段检查重复项。如果找到,只需丢弃重复项。由于归并排序是 O(n*log(n)),与简单的 O(n^2) 算法相比,这将提供改进的速度。
但是,要真正有效,您应该维护一个排序集并使其保持排序,这样您就可以跳过排序阶段并直接进入合并阶段。
【讨论】:
我不明白这如何解决查找哪些集合具有 2 个或更多共同元素的问题。这只是展示了如何找到两个集合的并集,我认为这是这个问题中更容易的部分。 我不认为知道一个集合是否有 2 个或更多的共同元素有帮助。由于您不知道有多少重复项,因此您无法停止检查它们。【参考方案3】:我不知道如何在少于 O(n^2) 的时间内完成。
每个集合都需要与其他集合进行比较,以查看它们是否包含 2 个或更多共享元素。这是 n*(n-1)/2 次比较,因此 O(n^2),即使检查共享元素需要恒定时间。
在排序中,简单的实现是 O(n^2),但您可以利用有序比较的传递性(例如,您知道快速排序的低分区中的任何内容都不需要与上分区,因为它已经与枢轴进行了比较)。这就是排序为 O(n * log n) 的结果。
这不适用于这里。因此,除非集合有什么特别之处,可以让我们跳过基于先前比较结果的比较,否则通常会是 O(n^2)。
保罗。
【讨论】:
如果集合中的元素是可以排序的,那么重复的项目将总是彼此相邻。这允许我们将搜索它们限制在相邻的项目上,一个 O(1) 操作,而不是每次都搜索它们,一个 O(n) 操作。 "集合中的元素可以排序..." 即使重复检测是 O(1),仍然需要 O(N^2) 比较。无论如何,我们不会在一组中寻找重复的项目。我们正在寻找在两组之间重复的项目。他们可能是第一个或最后一个或任何其他。 对集合中的元素进行排序,并不意味着重复项是相邻的。如果另一个集合中有相同的对,则一对是重复的。 我倾向于认为你是正确的保罗。我在上面所做的多通道操作可能具有 O(N*N) 行为。这确实是集合周围重复分布的函数。可能是 Set 1 和 2 合并,然后下一次通过 set 3 合并......一直到 N。有点像快速排序。【参考方案4】:附注:这取决于这种情况发生的频率。如果大多数集合对确实共享至少两个元素,那么在您逐步进行比较的同时构建新集合可能是最有效的,如果不这样做则将其丢弃符合条件。如果大多数对不共享至少两个元素,那么将新集合的构建推迟到条件确认后可能会更有效。
【讨论】:
【参考方案5】:如果您的元素本质上是数字的,或者可以自然排序(即,您可以分配一个值,例如 1、2、42 等...),我建议对合并集使用基数排序,并且再次通过以了解独特元素。
这个算法应该是 O(n),并且您可以使用按位移位运算符和位掩码对基数排序进行相当多的优化。我为我正在做的一个项目做了类似的事情,它就像一个魅力。
【讨论】:
以上是关于合并至少共享 2 个元素的集合的算法的主要内容,如果未能解决你的问题,请参考以下文章