如何有效地合并流中的int范围?

时间:2011-01-20 14:16:42

标签: algorithm merge stream

我们得到一个连续的整数范围流,如[1,3],[5,10],[2,6],...... 当每个新范围到来时,我们需要检查现有范围并查看它是否与任何现有范围重叠,如果发现任何重叠,则删除所有重叠范围并插入合并范围。我们需要一种有效的算法。请注意,范围一次只能形成一个流...

在面试中被问到这个问题。想法?

目的是将重叠范围合并为一个。例如,如果我们有3个范围按以下顺序排列:[1,3],[2,6],[5,10]。然后我们首先将前两个合并为[1,6],然后与第三个合并,它变为[1,10]。

5 个答案:

答案 0 :(得分:9)

此问题的标准算法是interval tree

答案 1 :(得分:-1)

当一个新元组进入(newstart,newend)时,在现有结束元素列表中执行二元搜索fon newstart-1,同样在现有开放元素列表中执行newend+1

与任何匹配范围合并。

如果没有范围匹配,请在两个最接近的范围之间插入。

更新:从头开始,我正在解决错误的问题:合并触摸范围。但重叠范围解决方案不会太不相似。

  1. 二进制搜索start(n) <= newstart
  2. 中最大的现有起始元素
  3. 二进制搜索end(n) >= newstart
  4. 中最小的现有结尾元素
  5. 如果两者返回相同的索引,则您的元组开始可与第n个条目合并,将newstart替换为start(n)
  6. 二进制搜索start(m) <= newend
  7. 中最大的现有起始元素
  8. 二进制搜索end(m) >= newend
  9. 中最小的现有结尾元素
  10. 如果两者返回相同的索引,则您的元组结尾可与第m个条目合并,将newend替换为end(m)
  11. 使用(newstart,newend)元组替换第n个和第m个索引之间的所有条目。

答案 2 :(得分:-1)

您可以将范围表示为交替“开”和“关”点的单个序列。每当新范围到达时,都会搜索其起点和终点以及合并或插入的相应操作。

为了获得所需操作的良好性能 - 搜索,插入和删除 - 在这里可能会使用类似B树的东西。

答案 3 :(得分:-1)

间隔树服务器目的很好,但这是我用来添加到范围的另一种方法。

假设添加新元组(rane)的列表为空或没有重叠范围。其次,输入的形式为a,b,其中a <= b 您可以将元组列表转换为单个数字列表,然后将新元组添加到其中。

让rangeList成为当前的范围列表。例如。 [1,4,6,10,12,14]是指[(1,4),(6,10),(12,14)]的范围列表

  1. 如果列表为空,只需在列表中插入元组的元素即可完成
  2. 使用二分查找在列表中查找a,b元素的位置。 (如果元素不存在,则返回最小元素的位置大于搜索到的数字)
  3. 让返回的位置分别为元组a,b的pos_a,pos_b。
  4. 如果pos_a为偶数,则list_A = [a]
  5. 如果pos_b是偶数,则list_B = [b]
  6. 新列表= rangeList [0:pos_a] + list_A + list_B + rangeList [b:end],你就完成了。
  7. 通过将元组列表转换为单个列表,我们无需将新元素与2个不同的列表进行比较。因此,通过检查奇数或偶数来轻松找到它的位置,告诉我们它是否位于现有范围之间

    def subroutine(rangeList, currTuple)
    """
    rangeList: Existing list of non overlapping tuples
    currTuple: The range tuple to be added
    """
        if not rangeList:
            rangeList.extend(currTuple)
        else:
            a, b = binSearch(currTuple, rangeList)
            list_changed = rangeList[0:a]
            if a%2 == 0:
                list_changed.append(currTuple[0])
            if b%2 == 0:
                list_changed.append(currTuple[1])
            list_changed.extend(rangeList[b:])
        return list_changed
    

答案 4 :(得分:-1)

随着时间间隔的流逝,它们必须保留在某种有效的集合中

  • 插入新间隔
  • 找到重叠间隔
  • 在添加新间隔时合并重叠间隔

合并操作需要适应以下情况

  
      
  • 无重叠(7, 9)插入[(1, 5), (10, 15)]会得到[(1,5), (7, 9), (10,15)]

  •   
  • 一些重叠(4, 6)插入[(1, 5), (10, 15)]会得到[(1,6), (10,15)]

  •   
  • 桥重叠(4, 11)插入[(1, 5), (10, 15), (20, 30)]会得到[(1, 15), (20, 30)]

  •   
  • 最大重叠(1, 20)插入[(2, 5), (7, 9), (13, 15)]会得到[(1, 20)]

  •   
  • 完全重叠(5, 6)插入[(1, 7), (10, 19)]会得到[(1, 7), (10, 19)]

  •   
  • 以及许多中间案例和上述内容的变体

  •   

在插入操作结束时,此集合必须仅包含非重叠间隔。显然,保持这些非重叠间隔在此集合中排序对于有效查找和合并重叠间隔是必要的。任何未排序的解决方案都需要在插入任何内容的每个时间间隔内进行搜索,然后在每次合并时都重复该过程。请记住,元素必须保持排序,任何类型的哈希或任何无序的数据结构也无济于事。我们在这里能做的最好的事情就是O(nlgn)

使用排序数组(或其数组列表或向量变体),一种方法是对新插入的间隔的开始进行二进制搜索,然后左右搜索并在必要时合并。这将导致O(nlgn)搜索元素的放置位置,但是,在插入数组中并从数组中删除元素时,无论何时发生插入或合并,都需要重新索引数组中的每个元素。这样的操作过于昂贵,无法很好地扩展,并且还破坏了将排序后的元素降级为原始未排序解决方案的性能的目的。

解决插入/删除问题的一种方法是使用排序的链表,其中插入和删除操作便宜,但这将使二分查找效率非常低,即使不是不可能,并且也会使保持元素排序的目的,再次降低了未处理的未排序解决方案的性能。

值得研究Wikipedia中定义的interval trees,但是该数据结构不会合并间隔,只能查询间隔,因此也不支持我们的用例。

最好的方法是使用二叉树存储间隔,其中间隔具有定义的比较器方法,如果存在重叠,则返回元素相等。这使得在O(lgn)时间内查找重叠间隔非常有效。

一个比较器类示例如下

public class Interval<T extends Comparable<T>> implements Comparable<Interval<T>>
{
    private final T start_;
    private final T end_;

    public Interval(final T start, final T end)
    {
        this.start_ = start;
        this.end_ = end;
    }

    @Override 
    public int compareTo(Interval other) {
        if (other.start_.compareTo(this.end_) == -1) { 
            return -1;
        } else if (other.start_.compareTo(this.end_) == 1) { 
            return 1; 
        } else { 
            return 0;
        }
    }
}

树集(或集合或其任何变体)可以扩展或组合以适合插入操作。请注意,由于Java不允许返回集合中的元素,因此我在下面使用了treemap变体。

public class IntervalCollection
{
    private Map<Interval, Interval> intervalCollection_ = new TreeMap<>();

    public void insert(Interval interval)
    {
        while (intervalCollection_.containsKey(interval)) {

            Interval other = intervalCollection_.get(interval);
            intervalCollection_.remove(interval);

            interval = new Interval(Math.min(interval.start_, other.start_),
                Math.max(interval.end_, other.end_));
        }

        intervalCollection_.put(interval, interval);
    }
}

此集合使用手头语言的功能为您完成大部分艰苦的工作,并为流传输间隔提供有效的插入和合并。