简体   繁体   English

在不相交间隔的排序列表中插入间隔

[英]Inserting an interval in a sorted list of disjoint intervals

I have a sorted list of disjoint intervals and an interval, eg [(1, 5), (10, 15), (20, 25)] and (12, 27). 我有一个不相交的间隔和间隔的排序列表,例如[[(1,5),(10,15),(20,25)]和​​(12,27)。 So, (12,27) is the interval I want to merge them into a sorted list of disjoint intervals: [(1, 5), (10, 27)]. 因此,(12,27)是我想要将它们合并为不相交间隔的排序列表的间隔:[(1,5),(10,27)]。

pseudo: 伪:

list = ...
u = (12,27)
i = 0
newlist = []

while (max(list[i]) < min(u))  //add all intervals before intersection
  newlist.add(list[i++])
mini = i

while (min(list[i]) < max(u))  // skip all intersecting intervals
  i++
maxi = i

newlist.add((min(list[mini],u),max(list[maxi],u)) // add the new interval

while (i < list.length) // add remaining intervals
  newlist.add(list[i++])

return newlist

Here is my non-idiomatic scala solution full of vars. 这是我的充满变量的非惯用的scala解决方案。 The solution looks longer than it should be because I have poor implementations of append and insert on lists which only support the prepend operation. 该解决方案看起来比应有的更长的时间,因为我在列表上执行append和insert的实现效果较差,这些列表仅支持prepend操作。

The algo is as follows: 算法如下:

  1. Scan to divide the list of intervals into two lists ones that don't overlap with the new interval and ones that do. 扫描以将间隔列表分为两个与新间隔不重叠的列表和一个与新间隔不重叠的列表。 Non-overlapping intervals are ones that begin either entirely after or entirely before the new interval. 非重叠间隔是完全在新间隔之后或完全在新间隔之前开始的间隔。 Also if we were to insert a new interval it would be after the highest interval that is lower than it. 同样,如果我们要插入一个新的时间间隔,则该时间间隔将在低于该时间间隔的最高时间间隔之后。 At this stage we have a list of overlapping intervals and a list of non-overlapping ones. 在此阶段,我们有一个重叠间隔的列表和一个不重叠间隔的列表。
  2. If there are no overlaps then the new interval either earlier than all given intervals or after all intervals or in between two intervals (still no overlap). 如果没有重叠,则新间隔早于所有给定间隔或在所有间隔之后或两个间隔之间(仍然没有重叠)。
  3. If there is an overlap merge the overlapping intervals with the new interval. 如果存在重叠,则将重叠间隔与新间隔合并。 The overlapping interval's start is min(new interval's start, smallest overlapping interval's start). 重叠间隔的开始为min(新间隔的开始,最小重叠间隔的开始)。 We can calculate the end similarly. 我们可以类似地计算结束时间。 Now insert the overlapping interval in the position calculated earlier. 现在,将重叠间隔插入之前计算出的位置。

Here is the code: 这是代码:

object trial {
  val intervals1 = List((1, 2), (4, 6), (7, 8), (16, 17)))
  val intervals2 = List((1, 5), (10, 15), (20, 25)

  val newInterval1 = (11, 12)
  val newInterval2 = (12, 27)

  // Interval algo test.
  val result1 = Merge(intervals1, newInterval1)   // result1  : List((1,2), (4,6), (7,8), (11,12), (16,17))
  val result2 = Merge(intervals2, newInterval2)   // result2  : List[(Int, Int)] = List((1,5), (10,27))
  }

object Merge{
  def append[T](list: List[T], el: T): List[T] = {
    (el :: list.reverse).reverse
  }
  def insert[T](list: List[T], pos: Int, elem: T): List[T] = {
    var newList = List.empty[T]
    val reversePos = list.length - pos
    list.reverse.zipWithIndex foreach {
      case(el, i) if i == reversePos => {
        newList = elem :: newList
        newList = el :: newList
      }
      case (el, i) => newList = el :: newList
    }
    newList
  }

  def apply(list: List[(Int, Int)], interval: (Int, Int)): List[(Int, Int)] = {
    val (min, max) = interval
    var newList = List.empty[(Int, Int)]
    // Store potentially overlapping stuff.
    var overlap = List.empty[(Int, Int)]
    // Maintain the position to begin merge.
    var posInsert = 0
    list.zipWithIndex foreach { case(intervalz, i) =>
      if (intervalz._2 < min) {
      // Does not overlap but an insert will be after the highest of these.
        posInsert = i
        newList = append(newList, intervalz)
      } else if (intervalz._1 > max) {
        // Does not overlap.
        newList = append(newList, intervalz)
      } else overlap = append(overlap, intervalz)
    }
    if (overlap isEmpty) {
      if (posInsert == 0) newList = interval :: newList
      else newList = insert(newList, posInsert + 1, interval)
    } else {
      // Start of new interval is the lower of the first overlap's start or the interval's start.
      val startOfInterval = Math.min(overlap.head._1, min)
      // Enf of interval is higher of last overlap's end or interval's end.
      val endOfInterval = Math.max(overlap.head._2, max)
      // Insert the merged interval after the highest interval smaller than the start of the new internval.
      if (posInsert == 0) newList = (startOfInterval, endOfInterval) :: newList
      else newList = insert(newList, posInsert + 1, (startOfInterval, endOfInterval))
    }
    newList
  }
}

It passes the tests mentioned here and is O(N). 它通过了此处提到的测试,并且为O(N)。

EDIT. 编辑。 The algo version of it: 它的算法版本:

intervals = [....]
newInterval = (12, 27)
newList = []
overlappingList = []
posInsert = 0
i = 0

while (i < intervals.size)
  if (intervals[i].max < newInterval.min)
    posInsert = i
    append(newList, intervals[i]) 
  else if (intervals[i].min > newInterval.max)
    append(newList, intervals[i])
  else
    append(overlappingList, intervals[i])
  i++

if (overlap isEmpty)
  if (posInsert == 0) prepend(newList, newInterval)
  else insert(newList, posInsert + 1, newInterval)
else
  start = Min(overlappingList[i].min, newInterval.min)
  end = Max(overlappingList[i].max, newInterval.max)
  if (posInsert == 0) prepend(newList, newInterval)
  else insert(newList, posInsert + 1, new Interval(start, end))
return newList

You can model your problem with graph, in fact your intervals are nodes and they are connected to each other if they have common parts (eg (12,27) is connected to (15,20)) now first you should find connected components, then in each connected component find min value of start (eg 10) and max value of end (eg 25). 您可以使用图形来建模问题,实际上,您的间隔是节点,并且如果它们具有相同的部分(例如(12,27)连接到(15,20)),则它们是相互连接的。现在,您首先应该找到连接的组件,然后在每个连接的组件中找到开始的最小值(例如10)和结束的最大值(例如25)。 It's good to see Interval Graphs . 看到间隔图很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM