在间隔列表中搜索间隔重叠?

时间:2010-12-15 02:06:43

标签: algorithm

假设[a,b]表示从a到b的实线上的间隔,a&lt; b,包括(即,[a,b] =所有x的集合,使得a <= x <= b)。另外,如果[a,b]和[c,d]共享任何x使得x在[a,b]和[c,d]中都是'重叠'。

给定一个区间列表,([x1,y1],[x2,y2],...),找到与[x,y]重叠的所有这些区间的最有效方法是什么?

显然,我可以尝试每个并在O(n)中获得它。但是我想知道我是否能够以一种聪明的方式对间隔列表进行排序,我可以通过二分搜索在O(log N)中找到/ one /重叠项目,然后从列表中的那个位置“环顾四周”找到所有重叠的间隔。但是,如何对间隔进行排序以使这种策略有效?

请注意,列表项中的元素之间可能存在重叠,这使得这很难。

我已经通过左边,右端,中间的间隔排序来尝试它,但似乎都没有导致详尽的搜索。

帮助?

6 个答案:

答案 0 :(得分:61)

为了完整起见,我想补充说,这种问题有一个众所周知的数据结构,已知(惊讶,惊讶)为interval tree。它基本上是一个增强的平衡树(红黑色,AVL,你的选择),它存储按左(低)端点排序的间隔。增强是每个节点在其子树中存储最大的右(高)端点。此树允许您在O(log n)时间内找到所有重叠的间隔。

在CLRS 14.3中有描述。

答案 1 :(得分:29)

[a,b]与[x,y]重叠,iff b> x和a&lt;年。按第一个元素对间隔进行排序可以为您提供与日志时间中第一个条件匹配的间隔。按最后一个元素对间隔进行排序可为您提供与日志时间中第二个条件匹配的间隔。取得结果集的交叉点。

答案 2 :(得分:4)

'quadtree'是一种数据结构,通常用于提高二维碰撞检测的效率。

我认为你可以想出一个类似的一维结构。这需要一些预先计算,但应该导致O(log N)性能。

基本上,您从一个覆盖所有可能间隔的根“节点”开始,并且在向树中添加节点时,您可以决定它是否位于中点的左侧或右侧。如果它越过中间点,则将其分成两个间​​隔(但记录原始父节点)并从那里递归地继续。您可以设置树的深度限制,这可以节省内存并提高性能,但代价是使事情变得复杂(您需要在节点中存储间隔列表)。

然后在检查间隔时,基本上找到插入的所有叶子节点,检查这些节点内的部分间隔以进行交叉,然后将记录的间隔报告为“原始”父节点。

答案 3 :(得分:1)

可以这么快就想到'脱掉袖口'。

你能否将它们组织成2个列表,一个用于间隔开始,另一个用于间隔结束。

这样,您可以将y与间隔开始列表中的项目(例如通过二分搜索)进行比较,以根据该项目减少候选项。

然后,您可以将x与间隔列表末尾的项目进行比较。

修改

案例:一旦关闭

如果您只是将一次性间隔与一次性情况下的间隔列表进行比较,我认为排序不会帮助您since ideal sorting is O(n)

通过对所有x进行线性搜索来修剪任何不可能的间隔,然后通过剩余的y进行另一次线性搜索,可以减少总工作量。虽然这仍然是O(n),但如果没有这个,你将进行2n次比较,而平均而言,你只会这样做(3n-1)/ 2次比较。

我相信这是你可以为未排序列表做的最好的事情。

案例:预排序不计算

如果您将重复比较单个间隔与此间隔列表并对列表进行预排序,则可以获得更好的结果。上面的过程仍然适用,但是通过在第一个列表上进行二进制搜索,然后第二个可以得到O(m log n)而不是O(mn),其中m是要比较的单个区间的数量。请注意,仍然可以为您提供减少总比较的优势。 [2m log n与m(3(log n)-1)/ 2]相比

答案 4 :(得分:0)

您可以同时按左端和右端排序,并使用两个列表来消除任何重叠值。如果列表按左端排序,则测试范围右端右侧的任何间隔都不能重叠。如果列表按右端排序,则测试范围左端左侧的任何间隔都不会重叠。

例如,如果间隔是

[1,4], [3,6], [4,5], [2,8], [5,7], [1,2], [2,2.5]

并且您发现与[3,4]重叠,然后按左端排序并标记测试右端的位置(右端比其值大,以便包含4在范围内)

[1,4], [1,2], [2,2.5], [2,8], [3,6], [4,5], *, [5,7]

你知道[5,7]不能重叠,然后按右端排序并标记测试左端的位置

[1,2], [2,2.5], *, [1,4], [4,5], [3,6], [5,7], [2,8]

您知道[1,2][2,2.5]不能重叠

不确定这是多么有效,因为你必须进行两种排序和搜索。

答案 5 :(得分:0)

正如您在其他答案中所看到的,大多数算法都带有特殊的数据结构。例如,对于未排序的间隔列表,因为输入O(n)是最好的。 (通常,根据决定算法的数据结构来考虑更容易)。

在这种情况下,您的问题不完整:

  • 您是否获得了整个列表,或者您实际创建了该列表?

  • 您是否只需执行一次此类查询或其中多项查询?

  • 您对它应支持的操作及其频率有任何估计吗?

例如,如果您只需执行一次此类查找,那么之前对列表进行排序是不值得的。如果很多,那么“1D四叉树”的更昂贵的分类或生成将被摊销。

然而,要解决这个问题会很困难,因为简单的四叉树(据我所知)只能检测碰撞,但它无法创建与输入重叠的所有细分的列表。

一个简单的实现是一个有序(通过coordonate)列表,您可以在其中插入带有标志开始/结束和段号的所有段末尾。通过这种方式,通过解析它(仍为O(n),但我怀疑如果你还需要重叠的所有段的列表,你可以使它更快),并保持所有未被关闭的已打开段的轨道“检查点“。