可能的面试问题:如何查找所有重叠间隔

Tom*_*ker 66 algorithm

这不是一个面试问题本身,因为我在我的项目中遇到过这个问题,但我认为这可能是一个不错的干预问题.

你有N对间隔,比如说整数.您需要在O(N)时间内识别出彼此重叠的所有间隔.例如,如果你有

{1,3} {12,14} {2,4} {13,15} {5,10}

答案是{1,3},{12,14},{2,4},{13,15}.请注意,您不需要对它们进行分组,因此结果可以按照示例中的任何顺序进行.

我刚刚投入O(N)时间因为KMP算法需要O(N)进行字符串搜索.:d

我想出的最好的,我现在在项目中使用的是O(N ^ 2).是的,蛮力非常难过,但没有人抱怨所以我不会重构它.:P仍然,我很好奇,如果一个更大的头脑有一个更优雅的解决方案.

mar*_*cog 95

将间隔的端点放入数组中,将它们标记为起点或终点.如果间隔是关闭的话,通过在起点之前放置终点来打破关系,或者如果它们是半开的则相反.

1S, 2S, 3E, 4E, 5S, 10E, 12S, 13S, 14E, 15E
Run Code Online (Sandbox Code Playgroud)

然后遍历列表,跟踪我们所处的间隔数(这相当于处理的起始点数减去终点数).每当我们在一个区间内遇到起点时,这意味着我们必须有重叠的间隔.

1S, 2S, 3E, 4E, 5S, 10E, 12S, 13S, 14E, 15E
    ^                          ^
   overlap                    overlap
Run Code Online (Sandbox Code Playgroud)

我们可以通过在端点旁边存储数据来找到哪些区间与哪些区间重叠,并跟踪我们所处的区间.

这是一个O(N logN)解决方案,其中排序是主要因素.

  • "通过在起点之前放置终点来打破联系" - 取决于如何定义间隔.如果它们是半开的,则`{1,2}和{2,3}`不重叠.如果它们是闭合间隔,那么这是一个重叠.问题没有说明哪个. (5认同)
  • @marcog:对此不确定,但算法真的是O(nlogn)吗?如果你需要返回哪些间隔相互重叠,那么它看起来更像是O(n ^ 2).当所有区间重叠时(如{1,8},{2,7},{3,6},{4,5}),O(n ^ 2)区间在解. (4认同)
  • @faizan 原发帖者提出的问题不清楚,这导致了混乱。正如 j_random_hacker 在顶部的评论中所说:“不可能在 O(N) 时间内报告所有重叠对,因为可能有 O(N^2) 个!OTOH 要求 O(N) 是合理的)大小的一组至少与其他间隔重叠的所有间隔”。marcog 和 gcbenison 都以 O(N log N) 的方式回答第二个问题 (2认同)

gcb*_*son 31

按起点对间隔进行排序.然后折叠该列表,如果它们重叠,则将每个间隔与其邻居(即(1,4),(2,6) - >(1,6))合并.结果列表包含没有重叠伙伴的那些间隔.从原始列表中筛选出这些内容.

在初始排序操作之后,这是线性的,可以使用任何(n log n)算法完成.不确定你是如何解决这个问题的.如果您利用第一个操作的输入和输出的已排序顺序,即使最后的"过滤重复"操作也是线性的.

这是Haskell中的一个实现:

-- Given a list of intervals, select those which overlap with at least one other inteval in the set.
import Data.List

type Interval = (Integer, Integer)

overlap (a1,b1)(a2,b2) | b1 < a2 = False
                       | b2 < a1 = False
                       | otherwise = True

mergeIntervals (a1,b1)(a2,b2) = (min a1 a2, max b1 b2)

sortIntervals::[Interval]->[Interval]
sortIntervals = sortBy (\(a1,b1)(a2,b2)->(compare a1 a2))

sortedDifference::[Interval]->[Interval]->[Interval]
sortedDifference [] _ = []
sortedDifference x [] = x
sortedDifference (x:xs)(y:ys) | x == y = sortedDifference xs ys
                              | x < y  = x:(sortedDifference xs (y:ys))
                              | y < x  = sortedDifference (x:xs) ys

groupIntervals::[Interval]->[Interval]
groupIntervals = foldr couldCombine []
  where couldCombine next [] = [next]
        couldCombine next (x:xs) | overlap next x = (mergeIntervals x next):xs
                                 | otherwise = next:x:xs

findOverlapped::[Interval]->[Interval]
findOverlapped intervals = sortedDifference sorted (groupIntervals sorted)
  where sorted = sortIntervals intervals

sample = [(1,3),(12,14),(2,4),(13,15),(5,10)]
Run Code Online (Sandbox Code Playgroud)


Nik*_*bak 8

针对互联网问题的标准方法是根据起点对它们进行排序,然后从头到尾进行排序.O(n*logn)(O(n)如果已经排序)

end = 0;
for (current in intervals) {
    if current.start < end {
        // there's an intersection!
        // 'current' intersects with some interval before it
        ...
    }
    end = max(end, current.end)
}
Run Code Online (Sandbox Code Playgroud)


jbx*_*jbx 5

不确定O(N),但是如果我们先按每个元组中的第一个数字对它们进行排序,然后顺序查找那些元组的第一个数字大于前一个元组中看到的最大数字的那个,那该怎么办?不与下一个元组重叠。

因此,您首先会得到:

{1,3},{2,4},{5,10},{12,14},{13,15}

因为4(最大)<5和10 <12,{5,10}是隔离的。

这将需要我们跟踪遇到的最大数字,并且每次找到起始编号较大的元组时,都要检查其是否与下一个重叠。

这将取决于排序算法的效率,因为后者的过程将是O(N)