【问题标题】:Given a set of intervals, find the interval which has the maximum number of intersections给定一组区间,找出交点数最多的区间
【发布时间】:2014-02-23 10:10:20
【问题描述】:

给定一组区间,找出具有最大交叉点数的区间(而不是特定交叉点的长度)。所以如果输入 (1,6) (2,3) (4,11), (1,6) 应该返回。有人建议使用间隔树在 O(nlogn) 中完成这项工作,但在阅读了它的 wiki 页面后,我不明白如何构造和使用间隔树。我相信可以通过某种排序和扫描算法来完成。如果区间树是唯一的选择,请教我如何构建/使用它。谢谢。

【问题讨论】:

  • 你不能希望在O(n) 下不进行预处理,因为你需要评估所有区间。
  • 对不起。错字。意味着输入 O(nlogn)

标签: algorithm data-structures


【解决方案1】:

不要使用区间树。为每个间隔端点创建一个事件,以便每个间隔都有一个开始事件和一个停止事件。按顺序处理这些事件。 (如果测量为零的交叉点算作交叉点,则订单在停止之前开始,否则在开始之前停止。)

初始化一个从区间到数字的映射 C。初始化开始计数 s = 0 和停止计数 t = 0。要处理间隔 i 的开始事件,请设置 s = s + 1,然后设置 C(i) = -(t + 1)。要处理间隔 i 的停止事件,请设置 t = t + 1,然后设置 C(i) = C(i) + s。

最后,C 将区间映射到它们的交点计数。这个算法是 O(n log n) 因为排序;如果只能通过相当标准的计算几何技术添加和比较端点,则运行时间是最佳的。

【讨论】:

  • 嗯,这很快。做得好。 (直到明天才能颁发赏金。)
  • 对于开始事件:C(i)=-1-num_stop_events_so_far,对于停止事件:C(i)+=num_start_events_so_far。不是更简单吗?
  • @EvgenyKluev 当然,为什么不呢?
  • Evgeny:准确点!这是不正确的。 numIntersections[interval] = StartedEventsTill[end[interval]] - EndedEventsTill[start[interval]]-1
  • @cegprakash 不是和他写的一样吗?间距不理想。
【解决方案2】:

注意:David Eisenstat 的算法比这个算法有更好的性能。

一个简单的平面扫描算法将在O(nlogn + m*n) 中执行此操作,其中m 是任何单个区间的最大交叉点数。

对区间端点进行排序。跟踪活动段。到达起点时,增加所有活动区间的交叉计数,并将新区间的交叉计数设置为活动区间的数量(不包括自身)。到达终点时,从活动区间中删除区间。

【讨论】:

  • 我明白了。这与我目前所拥有的几乎相同。但是我没有对端点进行排序,而是将其排序为 {(a1,start),(b1,end),(a2,start),(b2,end),...,(an,start),(bn,end )} (第一个坐标是数字,第二个坐标是标签)。按字典顺序对数组进行排序,从哪里开始。我认为这是 O(N^2),因为在遍历排序列表时,我们需要“增加所有活动区间的交叉点计数”,这需要 O(n) 并导致总 O(n^2)。
  • 抱歉,“对端点进行排序”是指起点和终点。
  • 但是你对运行时间提出了一个很好的观点。实际运行时间为O(nlogn + m*n),其中m是与输出区间的交叉点数。该算法也是O(n^2),但如果没有与所有其他区间相交的区间,则这是一个更宽松的界限。
  • 顺便说一下,AFAICT 一个区间树——至少,使用传统的区间树构造算法——不会改善这个界限
  • 所以这是我们能做的最好的吗? O(nlogn + m*n)?因为一个简单的蛮力也可以在 O(n^2) 中做到这一点......
【解决方案3】:

大卫方法的python 实现,

def max_interval_count(intervals):
    interval_sorted = []
    for idx, interval in enumerate(intervals):
        s, e = interval
        interval_sorted.append([s, idx, 0]) # 0 for start
        interval_sorted.append([e, idx, 1]) # 1 for end
    interval_sorted.sort(key = lambda x: x[0])

    print(interval_sorted)

    number_of_starts = 0
    number_of_ends = 0

    overlap_count = {} 
    for event in interval_sorted:
        _, idx, start_end = event
        if start_end == 0: # start event
            # subtract all the ending before it
            overlap_count[idx] = - (number_of_ends)
            number_of_starts += 1
        else: # end event
            overlap_count[idx] += (number_of_starts - 1) # -1 as we should not include the start from the same interval
            number_of_ends += 1
    print(overlap_count)
    ans_idx = -1
    max_over_count = 0
    min_len_interval = 99999999999
    for idx, overl_cnt in overlap_count.items():
        if overl_cnt > max_over_count:
            ans_idx = idx
            max_over_count = overl_cnt
        elif overl_cnt == max_over_count and overl_cnt > 0 and (intervals[idx][1] - intervals[idx][0] + 1) < min_len_interval:
            min_len_interval = (intervals[idx][1] - intervals[idx][0] + 1)
            ans_idx = idx
    if ans_idx == -1:
        return ans_idx
    return intervals[ans_idx]


if __name__ == "__main__":
    test_1 = [[1,5],[5,10],[5,5]]
    test_2 = [[1,2],[3,5]]
    test_3 = [(1,6), (2,3), (4,11)]
    ans = max_interval_count(test_1)
    print(ans)
    print("---------")
    ans = max_interval_count(test_2)
    print(ans)
    print("---------")
    ans = max_interval_count(test_3)
    print(ans)
    print("---------")
[[1, 0, 0], [5, 0, 1], [5, 1, 0], [5, 2, 0], [5, 2, 1], [10, 1, 1]]
{0: 0, 1: 1, 2: 1}
[5, 5]
---------
[[1, 0, 0], [2, 0, 1], [3, 1, 0], [5, 1, 1]]
{0: 0, 1: 0}
-1
---------
[[1, 0, 0], [2, 1, 0], [3, 1, 1], [4, 2, 0], [6, 0, 1], [11, 2, 1]]
{0: 2, 1: 1, 2: 1}
(1, 6)
---------

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多