Algorithm 按时间间隔合并范围

Algorithm 按时间间隔合并范围,algorithm,merge,tree,range,interval-tree,Algorithm,Merge,Tree,Range,Interval Tree,给定一组区间:{1-4,6-7,10-12}添加一个新区间:(9,11),以便最终的解“合并”:输出:{1-4,6-7,9-12}。合并可以在双方进行(低范围和高范围) 我看到这个问题在多个地方得到了回答,有人甚至建议使用间隔树,但没有解释他们将如何使用它。我所知道的唯一解决方案是按照开始时间的升序排列时间间隔,并对其进行迭代,并尝试适当地合并它们 如果有人能帮助我理解我们如何在这个用例中使用区间树,那就太好了 [我一直在关注CLRS书中的间隔树,但它们并没有讨论合并,它们只讨论插入和搜索。]查

给定一组区间:{1-4,6-7,10-12}添加一个新区间:(9,11),以便最终的解“合并”:输出:{1-4,6-7,9-12}。合并可以在双方进行(低范围和高范围)

我看到这个问题在多个地方得到了回答,有人甚至建议使用间隔树,但没有解释他们将如何使用它。我所知道的唯一解决方案是按照开始时间的升序排列时间间隔,并对其进行迭代,并尝试适当地合并它们

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


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

查看此内容。它可以帮助您:

图书馆提供以下功能:

1) 区间集

2) 分离间隔集

3) 分割间隔集(我假设这意味着间隔不能重叠,否则它们会被合并。)

实现这一点的一种方法是存储一个平衡的二叉搜索树,每个范围的端点有一个节点。然后,每个节点将被标记为一个“打开”节点(标记间隔的开始)或一个“关闭”节点(标记间隔的结束)

插入新范围时,将出现两种情况之一,即范围的起点:

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

    从这里开始,您需要确定范围的扩展程度。为此,请连续计算插入的初始节点的后续节点,直到出现以下情况之一:

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

  • 在范围结束后会看到一个闭合节点。在这种情况下,当新范围结束时,您处于现有范围的中间,因此您不需要再做任何事情。你完了

  • 在范围结束之前可以看到闭合或打开的节点。在这种情况下,您需要从树中删除该节点,因为旧范围包含在新范围中

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

  • 该算法的运行时是O(logn+klogn),其中n是间隔数,k是在该过程中删除的间隔数(因为必须执行n次删除)。但是,您可以使用以下技巧将其加速到O(logn)。由于删除过程始终删除序列中的节点,因此可以使用端点的后续搜索来确定要删除的范围的终点。然后,通过执行两个树拆分操作和一个树联接操作,可以将要从树中删除的子范围拼接到树外。在一个合适的平衡树上(例如,红黑或八字树),这可以在O(logn)总时间内完成,如果要包含很多范围,这会快得多


    希望这有帮助

    只需将相关的间隔添加到间隔集的末尾,然后对间隔集的所有元素执行合并即可

    此处详细介绍了合并操作:

    如果你没有心情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
    

    公共类合并间隔{

    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;   
    }
    
    公共静态类间隔{
    公共双启动;
    公共双端;
    公共间隔(双起点、双终点){
    this.start=start;
    this.end=end;
    }
    }
    公共静态列表mergeInteval(列表非重叠,间隔另一个){
    列表合并=新建ArrayList();
    用于(间隔电流:非重叠点){
    if(current.end
    C#

    公共类间隔
    {
    公共间隔(int start,int end){this.start=start;this.end=end;}
    公共int启动;
    公共互联网终端;
    }
    void AddInterval(列表、间隔)
    {
    int-lo=0;
    inthi=0;
    对于(lo=0;lo=list[lo]。start&&interval.start=list[hi]。start&&interval.end 0)
    {
    列表.拆卸范围(lo+1,hi-lo);
    }
    }
    
    谢谢,但我更感兴趣的是算法,而不是现成的库/API。一个简单的算法可能是,您可以首先通过起始值对范围进行排序,然后从头到尾迭代范围,每当您发现一个范围与下一个重叠时,将其合并。是的,我已经知道了。引用根据我的问题:“我所知道的唯一解决方案是按照间隔开始时间的升序排列间隔,并对其进行迭代,并尝试将其适当合并。”。我正在寻找一个更优化的解决方案(可能涉及间隔树?)这个答案:提到了合并间隔树的算法对于起始点插入情况:首先检查具有
    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;   
    }
    
    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);
        }
    }