我最近遇到了一个有趣的问题:
给定两个间隔列表,找到两个列表中的重叠间隔总数。
Example
L1: ([1,2][2,3][4,5][6,7])
L2: ([1,5][2,3][4,7][5,7])
[1,5] overlaps [1,2] [2,3] [4,5]
[2,3] overlaps [1,2] [2,3]
[4,7] overlaps [4,5] [6,7]
[5,7] overlaps [4,5] [6,7]
total = 3+2+2+2 = 9
显然蛮力方法有效,但速度太慢(我需要比O(n ^ 2)更好的东西)。
我也喜欢类似的问题here。但它并不完全一样......
感谢任何帮助
答案 0 :(得分:3)
尝试寻找扫描线算法,它将为您提供最快的解决方案。
您可以在TopCoder site查看简短说明或观看video from Robert Sedgwick。这些描述了一个更难的问题,但应该给你一个如何解决你的方法。
实际上,主要的想法是每次更新特殊交叉列表中的段列表时,遍历段的开头和结尾的排序列表。
对于此任务,您将分别为每个原始列表分别有两个交叉点列表。在开始时,两个交叉点列表都是空的。在越过段的开头时,将其添加到适当的交叉点列表,它显然与另一个交叉点列表中的所有段相交。当到达段的末尾时,只需将其从交叉点列表中删除。
此算法将为您提供O(n log(n))速度,最坏情况下为O(n)内存。
答案 1 :(得分:3)
使用(value; +1 or -1 for start and end of interval)
对制作两个已排序的列表。
两个计数器 - Count1
和Count2
,显示第一个和第二个列表中的活动间隔数。
以合并方式遍历两个列表。
当您从第一个列表中获得+1时加1对{ - Count1
当您从第一个列表中获得对-1时 - 减少Count1
并将Count2
添加到结果
第二个列表中的对
相同最后阶段的伪代码
CntA = 0
CntB = 0
Res = 0
ia = 0
ib = 0
while (ia < A.Length) and (ib < B.Length)
if Compare(A[ia], B[ib]) <= 0
CntA = CntA + A[ia].Flag
if (A[ia].Flag < 0)
Res = Res + CntB
ia++
else
CntB = CntB + B[ib].Flag
if B[ib].Flag < 0
Res = Res + CntA
ib++
微妙时刻 - 比较if Compare(A[ia], B[ib]) <= 0
我们在这里也应该考虑标志 - 正确处理端点只触及[1..2] [2..3]的情况(你认为这种情况是交集)。因此,排序和合并比较器应该采用如下合成值:3 * A[ia].Value - A[ia].Flag
。通过这样的比较,在具有相同坐标的间隔结束之前处理间隔的开始。
P.S。在Delphi中进行快速测试。适用于给定的数据集和其他数据集。
Delphi code(由于仿制药,FPC没有编译它)
答案 2 :(得分:0)
您可以在第二个数组的循环中使用std :: set_intersection,以使其与第一个数组中的每个项匹配。但我不确定性能是否符合您的要求。
答案 3 :(得分:0)
我最近在处理类似问题时偶然发现了Interval Tree ADT - 我怀疑它对你有用,无论你是否实现它。
它基本上是一个三元树,我使用包含以下内容的节点构建它:
在O(n*log(n))
中构建树后,查询重叠间隔的查询函数应为O(log(n) + m)
,其中m
是报告的重叠间隔数。
注意在创建时,按间隔中的结束值排序并拆分列表应有助于保持平衡。