在重叠区间中查找基本区间

时间:2011-12-14 08:30:17

标签: algorithm data-structures

在准备一些编程访谈时,我遇到了一个很好的问题。

给定一组可能重叠的区间,您需要编写一个函数来返回它们之间的所有基本区间。例如:如果给定间隔作为以下对列表:{{1,5},{3,10},{5,11},{15,18},{16,20}},那么您需要返回以下内容:

{{1,3},{3,5},{5,10},{10,11},{15,16},{16,18},{18,20}}

请注意以上答案中的以下内容:

  • 答案中省略了间隔{11,15},因为它是 输入中不存在。
  • 输入的间隔{1,5}已分为{1,3},{3,5} 在答案中因为{3,10}中定义的起点“3” 将间隔缩小为两个基本区间的输入。

Java中的方法签名:

List<Pair<Integer, Integer>> generateElementaryIntervals(List<Pair<Integer, Integer> intervals)

我想象的一个解决方案是将输入分成非交叉集,然后对每个非交叉集中的所有数字进行简单的O(NlogN)排序将得出答案。有更有效的方法吗?

3 个答案:

答案 0 :(得分:8)

您可以先将此问题分解为嵌套间隔,然后分别处理每个嵌套。通过嵌套,我的意思是至少共享一个点的间隔。对于您给出的示例:

{{1,5}, {3,10}, {5,11}, {15,18}, {16,20}}

有两种嵌套:

{1,5}, {3,10}, {5,11}

{15,18}, {16,20}

通常,要确定嵌套,您可以根据左端点对间隔进行排序(如示例所示),然后在看到{x,y}, {x',y'} y < x'时运行并开始新的嵌套

对于嵌套,“基本区间”由值的排序序列(不重复)形成。在示例中,嵌套提供

(1,3,5,10,11) -> {1,3}, {3,5}, {5,10}, {10,11}

(15,16,18,20) -> {15,16}, {16,18}, {18,20}

因此整体算法可能如下所示:

  • 根据左端点
  • 对间隔进行排序
  • 使用{x,y}, {x',y'}
  • 运行间隔,直至y < x'
  • 从开始到{x,y},制作已排序的端点列表(无重复),比如a0,a1,...,ak
  • {ai,a(i+1)}
  • 添加基本区间i = 0...k-1
  • 删除最多{x,y}的时间间隔,然后从第2步继续

答案 1 :(得分:1)

您可以对端点进行排序,然后按顺序迭代。为了知道您是否在场,您可以保留覆盖每个点的间隔数。 间隔的左端贡献+1,而右边贡献-1: (请注意,我使用TreeMap,已排序)

static class Pair<T, K> {
    public Pair(T first, K second){
        this.first = first;
        this.second = second;
    }

    public String toString(){
        return "(" + first + ", " + second + ")";
    }

    T first;
    K second;   
}    

static List<Pair<Integer, Integer>> generateElementaryIntervals(List<Pair<Integer, Integer>> intervals) {
    TreeMap<Integer, Integer> overlaps = new TreeMap<Integer, Integer>();
    for(Pair<Integer, Integer> interval : intervals){
        int value = overlaps.containsKey(interval.first) ? overlaps.get(interval.first)+1 : 1;
        overlaps.put(interval.first, value);

        value = overlaps.containsKey(interval.second) ? overlaps.get(interval.second)-1 : -1;
        overlaps.put(interval.second, value);
    }

    List<Pair<Integer, Integer>>  retValue = new ArrayList<Pair<Integer,Integer>>();

    int overlap = 0;
    boolean in = false;
    int last = 0;
    for(int point : overlaps.keySet()){
        if(in)
            retValue.add(new Pair(last, point));

        overlap += overlaps.get(point);
        last = point;
        in = overlap > 0;
    }

    return retValue;
}    

public static void main(String[] args) {

    List<Pair<Integer, Integer>> l = new ArrayList<Pair<Integer, Integer>>();
    l.add(new Pair<Integer, Integer>(1,5));
    l.add(new Pair<Integer, Integer>(3,10));
    l.add(new Pair<Integer, Integer>(5,11));
    l.add(new Pair<Integer, Integer>(15,18));
    l.add(new Pair<Integer, Integer>(16,20));

    for(Object o : generateElementaryIntervals(l)){
        System.out.println(o.toString());
    }

}

答案 2 :(得分:0)

一个简单的算法就是简单地读取整个数字列表,并为每对中的每个项目创建一个元素。

每个元素都会存储两个值:number,以及它是第一个还是第二个数字(来自输入)。

然后对这些对进行排序,首先是内部number,然后按其位置(second将在first之前进行排序)

要打印出间隔列表,您可以使用以下规则打印每个数字和下一个数字:

  • 您不会打印重复的数字(例如,您不会打印5,5)
  • 如果您只拥有second个数字,然后是first个数字,则不会打印该基本间隔,因为该范围内没有值。