按间隔合并范围

时间:2013-01-27 08:27:03

标签: algorithm merge tree range interval-tree

给定一组间隔:{1-4,6-7,10-12}添加一个新的间隔:(9,11),以便最终解决方案'合并':输出:{1-4,6 -7,9-12}。合并可能发生在双方(低范围和高范围)。

我看到这个问题在多处被回答,有人甚至建议使用Interval Tress,但没有解释他们究竟会如何使用它。我所知道的唯一解决方案是按照开始时间的升序排列间隔并迭代它们并尝试合适地合并它们。

如果有人可以帮助我理解我们如何在这个用例中使用区间树,那就太棒了!

[我一直关注CLRS书中的间隔树,但他们没有谈论合并,他们谈论的只是插入和搜索。]

5 个答案:

答案 0 :(得分:6)

(我假设这意味着间隔永远不会重叠,否则它们会被合并。)

这样做的一种方法是存储一个平衡的二进制搜索树,每个端点的一个节点。然后将每个节点标记为标记间隔开始的“打开”节点或标记间隔结束的“关闭”节点。

插入新范围时,会出现关于范围起点的两种情况之一:

  1. 它已经在一个范围内,这意味着您将扩展已存在的范围作为插入的一部分。
  2. 它不在范围内,因此您将创建一个新的“开放”节点。
  3. 要确定您所处的情况,您可以在树中执行前导搜索范围的起点。如果获得NULL或关闭节点,则需要插入表示范围起点的新打开节点。如果你得到一个开放节点,你将继续延长该间隔。

    从那里,您需要确定范围延伸的范围。为此,请连续计算插入的初始节点的后继节点,直到出现以下情况之一:

    1. 您已查看树中的所有节点。在这种情况下,您需要插入一个标记此间隔结束的关闭节点。

    2. 您会在范围结束后看到关闭节点。在这种情况下,当新范围结束时,您处于现有范围的中间,因此您无需再执行任何操作。你已经完成了。

    3. 您会在范围结束前看到关闭或打开的节点。在这种情况下,您需要从树中删除该节点,因为旧范围包含在新范围内。

    4. 您会在范围结束后看到一个打开的节点。在这种情况下,请在树中插入一个新的关闭节点,因为在看到这个新节点的开始之前需要终止当前范围。

    5. 天真地实现,该算法的运行时间为O(log n + k log n),其中n是间隔数,k是在此过程中删除的间隔数(因为您必须执行n次删除)。但是,您可以使用以下技巧将其加速到O(log n)。由于删除过程始终会删除序列中的节点,因此可以使用端点的后继搜索来确定要删除的范围的结尾。然后,您可以通过执行两个树分割操作和一个树连接操作来拼接子范围以从树中移除。在合适的平衡树(例如红黑或展开)上,这可以在O(log n)总时间内完成,如果很多范围将被包含在内,则速度会快得多。

      希望这有帮助!

答案 1 :(得分:1)

公共类MergeIntervals {

public static class Interval {

    public double start;
    public double end;

    public Interval(double start, double end){
        this.start = start;
        this.end = end;
    }
}

public static List<Interval> mergeInteval(List<Interval> nonOverlapInt, Interval another){

    List<Interval> merge = new ArrayList<>();

    for (Interval current : nonOverlapInt){

        if(current.end < another.start || another.end < current.start){
            merge.add(current);
        }
        else{

            another.start = current.start < another.start ? current.start : another.start ;
            another.end = current.end < another.end ? another.end : current.end;                
        }           
    }
    merge.add(another);
    return merge;   
}

答案 2 :(得分:0)

检查一下。它可以帮助您: - http://www.boost.org/doc/libs/1_46_0/libs/icl/doc/html/index.html

图书馆提供以下功能:

1)interval_set

2)separate_interval_set

3)split_interval_set

答案 3 :(得分:0)

C#

public class Interval
{
    public Interval(int start, int end) { this.start = start; this.end = end; }
    public int start;
    public int end;
}

void AddInterval(List<Interval> list, Interval interval)
{
    int lo = 0;
    int hi = 0;
    for (lo = 0; lo < list.Count; lo++)
    {
        if (interval.start < list[lo].start)
        {
            list.Insert(lo, interval);
            hi++;
            break;
        }
        if (interval.start >= list[lo].start && interval.start <= list[lo].end)
        {
            break;
        }
    }
    if (lo == list.Count)
    {
        list.Add(interval);
        return;
    }

    for (hi = hi + lo; hi < list.Count; hi++)
    {
        if (interval.end < list[hi].start)
        {
            hi--;
            break;
        }
        if (interval.end >= list[hi].start && interval.end <= list[hi].end)
        {
            break;
        }
    }
    if (hi == list.Count)
    {
        hi = list.Count - 1;
    }

    list[lo].start = Math.Min(interval.start, list[lo].start);
    list[lo].end = Math.Max(interval.end, list[hi].end);

    if (hi - lo > 0)
    {
        list.RemoveRange(lo + 1, hi - lo);
    }
}

答案 4 :(得分:-1)

这可以通过将相关区间添加到区间集的末尾,然后对区间集的所有元素执行合并来完成。

此处合并操作非常详细:http://www.geeksforgeeks.org/merging-intervals/

如果您不喜欢C ++代码,python中的内容也是如此:

def mergeIntervals(self, intervalSet):
    # interval set is an array.
    # each interval is a dict w/ keys: startTime, endTime.  
    # algorithm from: http://www.geeksforgeeks.org/merging-intervals/
    import copy
    intArray = copy.copy(intervalSet)
    if len(intArray) <= 1:
        return intArray
    intArray.sort(key=lambda x: x.get('startTime'))
    print "sorted array: %s" % (intArray)
    myStack = []  #append and pop.
    myStack.append(intArray[0])
    for i in range(1, len(intArray)):
        top = myStack[0]
        # if current interval NOT overlapping with stack top, push it on.
        if   (top['endTime'] < intArray[i]['startTime']):
            myStack.append(intArray[i])
        # otherwise, if end of current is more, update top's endTime
        elif (top['endTime'] < intArray[i]['endTime']):
            top['endTime'] = intArray[i]['endTime']
            myStack.pop()
            myStack.append(top)

    print "merged array: %s" % (myStack)
    return myStack

不要忘记你的鼻子测试,以确认你确实做了正确的工作:

class TestMyStuff(unittest.TestCase):

    def test_mergeIntervals(self):
        t = [ { 'startTime' : 33, 'endTime' : 35 }, { 'startTime' : 11, 'endTime' : 15  }, { 'startTime' : 72, 'endTime' : 76 }, { 'startTime' : 44, 'endTime' : 46  } ]
        mgs = MyClassWithMergeIntervalsMethod()
        res = mgs.mergeIntervals(t)
        assert res == [ { 'startTime' : 11, 'endTime' : 15  }, { 'startTime' : 33, 'endTime' : 35 }, { 'startTime' : 44, 'endTime' : 46  }, { 'startTime' : 72, 'endTime' : 76 } ]

        t = [ { 'startTime' : 33, 'endTime' : 36 }, { 'startTime' : 11, 'endTime' : 35  }, { 'startTime' : 72, 'endTime' : 76 }, { 'startTime' : 44, 'endTime' : 46  } ]
        mgs = MyClassWithMergeIntervalsMethod()
        res = mgs.mergeIntervals(t)
        assert res == [{'endTime': 36, 'startTime': 11}, {'endTime': 46, 'startTime': 44}, {'endTime': 76, 'startTime': 72}]