【问题标题】:Union of intervals区间并集
【发布时间】:2010-11-05 07:24:58
【问题描述】:

我有一个代表区间的类。此类具有可比较类型的两个属性“开始”和“结束”。现在我正在寻找一种有效的算法来合并一组这样的间隔。

提前致谢。

【问题讨论】:

    标签: union intervals


    【解决方案1】:

    按其中一个术语(例如 start)对它们进行排序,然后在列表中移动时检查与它的(右侧)邻居是否重叠。

    class tp:
        def __repr__(self):
            return "(%d,%d)" % (self.start, self.end)
    
        def __init__(self, start, end):
            self.start = start
            self.end = end
    
    
    s = [tp(5, 10), tp(7, 8), tp(0, 5)]
    s.sort(key=lambda self: self.start)
    y = [s[0]]
    for x in s[1:]:
        if y[-1].end < x.start:
            y.append(x)
        elif y[-1].end == x.start:
            y[-1].end = x.end
    

    【讨论】:

    • 我认为最后一个elif 语句应该是寻找重叠,不一定是严格等于;然后最终分配需要取y[-1].endx.end 中的较大者。例如,请参阅以下内容:s=[tp(1,4),tp(6,8),tp(7,10)]
    【解决方案2】:

    在c++中求区间并集的总和

    #include <iostream>
    #include <algorithm>
    
    struct interval
    {
        int m_start;
        int m_end;
    };
    
    int main()
    {
        interval arr[] = { { 9, 10 }, { 5, 9 }, { 3, 4 }, { 8, 11 } };
    
        std::sort(
            arr,
            arr + sizeof(arr) / sizeof(interval),
            [](const auto& i, const auto& j) { return i.m_start < j.m_start; });
    
        int total = 0;
        auto current = arr[0];
        for (const auto& i : arr)
        {
            if (i.m_start >= current.m_end)
            {
                total += current.m_end - current.m_start;
                current = i;
            }
            else if (i.m_end > current.m_end)
            {
                current.m_end = i.m_end;
            }
        }
    
        total += current.m_end - current.m_start;
        std::cout << total << std::endl;
    }
    

    【讨论】:

      【解决方案3】:

      事实证明这个问题已经解决了很多次——在不同层次的幻想下,按照命名法:http://en.wikipedia.org/wiki/Interval_treehttp://en.wikipedia.org/wiki/Segment_tree,还有'RangeTree'

      (因为 OP 的问题涉及大量间隔,这些数据结构很重要)


      就我自己对python库的选择而言:

      • 通过测试,我发现最能说明问题的是功能齐全和 python 当前(非位腐烂):来自 SymPy 的“Interval”和“Union”类,请参阅:@987654323 @

      • 另一个好看的选择,性能更高但功能较少的选项(例如,不适用于浮点范围删除):https://pypi.python.org/pypi/Banyan

      最后:搜索 SO 本身,在 IntervalTree、SegmentTree、RangeTree 中的任何一个下,你会发现更多的答案/钩子

      【讨论】:

        【解决方案4】:

        geocar 的算法在以下情况下失败:

        s=[tp(0,1),tp(0,3)]
        

        我不太确定,但我认为这是正确的方法:

        class tp():
            def __repr__(self):
                return '(%.2f,%.2f)' % (self.start, self.end)
            def __init__(self,start,end): 
                self.start=start
                self.end=end
        s=[tp(0,1),tp(0,3),tp(4,5)]
        s.sort(key=lambda self: self.start)
        print s
        y=[ s[0] ]
        for x in s[1:]:
            if y[-1].end < x.start:
                y.append(x)
            elif y[-1].end == x.start:
                y[-1].end = x.end
            if x.end > y[-1].end:
                y[-1].end = x.end
        print y
        

        我也实现了减法:

        #subtraction
        z=tp(1.5,5) #interval to be subtracted
        s=[tp(0,1),tp(0,3), tp(3,4),tp(4,6)]
        
        s.sort(key=lambda self: self.start)
        print s
        for x in s[:]:
            if z.end < x.start:
                break
            elif z.start < x.start and z.end > x.start and z.end < x.end:
                x.start=z.end
            elif z.start < x.start and z.end > x.end:
                s.remove(x)
            elif z.start > x.start and z.end < x.end:
                s.append(tp(x.start,z.start))
                s.append(tp(z.end,x.end))
                s.remove(x)
            elif z.start > x.start and z.start < x.end and z.end > x.end:
                x.end=z.start
            elif z.start > x.end:
                continue
        
        print s
        

        【讨论】:

          【解决方案5】:

          对所有点进行排序。然后遍历列表,增加“开始”点的计数器,并减少“结束”点的计数器。如果计数器达到 0,那么它确实是联合中某个区间的端点。

          计数器永远不会变为负数,并且会在列表末尾达到 0。

          【讨论】:

            【解决方案6】:

            使用sweep line 算法。基本上,您对列表中的所有值进行排序(同时保持它是间隔的开始还是结束以及每个项目)。这个操作是 O(n log n)。然后你遍历排序的项目并计算间隔 O(n)。

            O(n log n) + O(n) = O(n log n)

            【讨论】:

              猜你喜欢
              • 2015-07-16
              • 2015-07-21
              • 2017-08-24
              • 1970-01-01
              • 2014-11-04
              • 1970-01-01
              • 1970-01-01
              • 2020-08-25
              相关资源
              最近更新 更多