【问题标题】:search for interval overlap in list of intervals?在间隔列表中搜索间隔重叠?
【发布时间】:2010-12-15 02:06:42
【问题描述】:

说[a,b]表示从a到b的实线上的区间,a

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

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

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

我已经尝试通过按左端、右端、中间对间隔进行排序,但似乎没有一个会导致详尽的搜索。

帮助?

【问题讨论】:

  • +1,因为它让我的工作日更加有趣。

标签: algorithm


【解决方案1】:

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

在 CLRS 14.3 中有描述。

【讨论】:

    【解决方案2】:

    [a, b] 与 [x, y] 重叠当且仅当 b > x 和 a

    【讨论】:

    • 交叉路口不会花费 O(n) 时间吗?如果列表有 N 个元素长,则与列表中的“相似大小”的查询间隔将在第一个列表中产生 m 个匹配项,在第二个列表中产生最多 N-m 个匹配项,并且获取交集将需要遍历所有 N 个元素,不会吗?
    • 它需要 O(与条件匹配的较小项目列表的大小)。如果所有区间的一小部分匹配,比如 f(N),则需要 O(f(N))。我认为会是这种情况——否则我认为任何策略都大约是 O(N)。
    • 这不是最好的答案;您在此处描述的内容属于此页面上的“幼稚方法”:en.wikipedia.org/wiki/Interval_tree。事实上,另一个更正确的答案建议研究区间树。
    • 重叠检测条件需要>=和
    • @max:遍历较小的集合;对于每个元素,检查它是否在更大的集合中(使用诸如哈希表之类的常量成员查询)。
    【解决方案3】:

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

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

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

    然后在检查间隔时,您基本上会找到它插入时将插入的所有叶节点,检查这些节点内的部分间隔是否相交,然后将记录的间隔报告为“原始”父母。

    【讨论】:

    • 这种类似的结构,奇怪的是,被称为二叉树。
    • @Nick Wiggill - 当然,它是一种二叉树,但二叉树有很多用途,我描述的算法更详细一些。
    【解决方案4】:

    可以这么说,只是“即兴发挥”的快速思考。

    您能否将它们组织成 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]

    【讨论】:

    • 您在答案顶部的初步建议属于此页面上的“幼稚方法”:en.wikipedia.org/wiki/Interval_tree。
    【解决方案5】:

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

    例如,如果间隔是

    [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] 不能重叠

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

    【讨论】:

      【解决方案6】:

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

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

      • 是你给出了整个列表还是你实际创建了它?

      • 您必须执行一次这样的查找还是执行多次?

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

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

      但是,解决它会很困难,因为一个简单的四叉树(据我所知)只能检测到碰撞,但它无法创建与您的输入重叠的所有段的列表。

      一个简单的实现是一个有序(按坐标)列表,您可以在其中插入所有以标志开始/结束和段号结尾的段。通过这种方式,通过解析它(仍然是 O(n),但如果您还需要所有重叠段的列表,我怀疑您是否可以使其更快),并跟踪所有未关闭的打开段“检查点”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-10
        • 1970-01-01
        • 2020-06-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多