【问题标题】:Combining intersecting intervals [duplicate]结合相交区间[重复]
【发布时间】:2012-09-10 17:06:01
【问题描述】:

可能重复:
Union of intervals
how to overlap intervals efficiently

给定一个区间列表表示所有整数,如果它们确实相交或重叠,则应该可以将它们折叠成一个区间,否则给定的区间不受影响。 比如说,如果输入是例如 I [(2-6), (1-4), (8-12)],那么预期的输出是 [(1-6), (8-12)] 例如II [(4-7), (2-6), (1-4), (8-12), (7-9)] 预期输出为 [(1-12)]。

更正:错过了排序部分,所以是的,这是 O(nlogn) 时间而不是 O(n)。感谢您指出了这一点。 我已经编写并测试了一个有效的 O(nlogn) 时间和 O(2n) 空间算法方法。在下面分享这种方法的代码。我有兴趣听到解决这个问题的不同方法,可能更有效。

//假设每个区间,(2-6)等都表示为“区间”对象(类定义如下图),其中low = 2 and high = 6 // Step1:按给定区间的低端点排序 // Step2: 求排序区间的并集

//输入:

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

//输出:

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

private static final Comparator<Interval> Low_EPSorter = new LowEPSorter();

class Interval {
  int low, high;

  Interval(int l, int h, int m) {
    low = l;
    high = h;
  }
}

////-------BEGIN:找到给定区间的并集的方法----//////

void UnionOfIntervals() {
  //Find intersection and combine intervals as necessary
  int sz = intervalList.size();

  // sort by low endpoint
  Collections.sort(intervalList, Low_EPSorter);

      for(int i = 0; i < sz; i++) {
        int j = i;
        if(j > 0) {
          if( Intervals.intersect(intervalList.get(j), intervalList.get(j-1)) ) {
            Interval v = union(intervalList.get(j), intervalList.get(j-1));
            checkAndAdd(v, unionList);
          }
          else {
            if(i == 1) {
              unionList.add(intervalList.get(j-1));
              unionList.add(intervalList.get(j));
          }
          else {
            unionList.add(intervalList.get(j));
          }
        } //No intersection
      } //If 2 elements atleast
      }

      //Print intervals after union
      System.out.println("Input intervals after sorting:");
      for(Interval v : intervalList) {
        System.out.print(v.low + "," + v.high + " ");
      }
      System.out.println();
      System.out.println("Union of intervals:");
      for(Interval v : unionList) {
        System.out.print(v.low + "," + v.high + " ");
      }
    }

    void checkAndAdd(Interval x, List t) {
      int top = t.size()-1;
      if( top >=0 && Intervals.intersect(unionList.get(top), x) ) {
        Interval v = union(unionList.get(top), x);
        t.remove(top);
        t.add(v);
      }
      else {
        t.add(x);
      }
    }

////-------END:找到给定区间的并集的方法----//////

////--- 辅助方法--- ////

static boolean intersect(Interval a, Interval b) {
      boolean r = false;
      if(b.high < a.low || b.low > a.high)
        r = false;
      else if(a.low <= b.high && b.low <= a.high)
        r = true;
      return r;
}

Interval union(Interval a, Interval b) {
      int l = (a.low < b.low) ? a.low : b.low;
      int max = (a.high > b.high) ? a.high : b.high;
      return new Interval(l, max);
}

private static class LowEPSorter implements Comparator<Interval> {

      public int compare(Interval a, Interval b) {
        int r = 0;
        if(a.low < b.low)
          r = -1;
        else if(a.low > b.low)
          r = 1;
        return r;
      }

}

【问题讨论】:

  • 我的 java 体验不是最好的,但是由于docs.oracle.com/javase/1.4.2/docs/api/java/util/…, java.util.Comparator) 你还没有编写在 O(n) 中运行的算法,但是 O(n *登录 n) 代替
  • -1 表示在输入此问题时未研究且未注意 SO 提供的建议重复项。这已在 SO 上多次回答
  • 可以在 O(n) 中求解,但区间中的最高数应与 n 相当。
  • @AkashdeepSaluja:可以在 O(n) 中解决,如果选择基于非比较的排序,如计数排序,输入中最高数字的知识有帮助?
  • @sangv 是的,可以说这个想法基本上是计数排序,但不完全是。我也发布了我的建议作为答案。

标签: java algorithm intervals


【解决方案1】:

取一个大小为 n 的数组(其中 n 是最大数),并分别用 1 和 -1 填充区间 start 和 end。

我的意思是如果间隔是

{[1-4],[6-8]} 

那么数组元素会是这样的

array[1]=1,array[4]=-1,array[6]=1,array[8]=-1

其余所有其他位置的数组将设置为零。

现在遍历数组,通过扫描数组,我们可以得到间隔,就像案例一样

{[1-4],[2-5],[7-9]},

首先按上述方式填充数组,数组 A 的样子(假设起始索引为 1):

A=[1,1,0,-1,-1,0,1,0,1]

现在从头开始遍历数组 A 并取一个变量 sum=0 并将存储在数组位置的值添加到 sum。

说明数组每个索引处的总和:

在位置 1:sum = 1(1 在索引 1 处)

在位置 2:sum = 2(1 在索引 2 处)

在位置 3:sum = 2(索引 3 处为 0)

在位置 4:sum = 1(-1 在索引 4 处)

在位置 5:sum = 0(-1 在索引 5 处)

现在总和达到零,这意味着一个区间在这里结束,所以新的区间将是 [1-5]

在位置 6:sum = 0(索引 6 处为 0)

在位置 7:sum = 1(索引 7 处为 1)

(在位置 7 处,总和再次大于零,这意味着间隔刚刚开始)

在位置 8:sum = 1(索引 8 处为 0)

在位置 9:sum = 0(在索引 9 处为 -1)

从位置 7 开始的间隔刚刚结束,因此新的间隔范围将是

{[1-5],[7-9]}

希望对你有帮助。

【讨论】:

  • 仅供参考,这不适用于以下测试用例(即,如果您有 dups):(-1, -1), (-1, -1), (3, 5), (6, 6), (3, 5)
【解决方案2】:

如果你正在为这个问题寻找比 O(n) 更有效的算法,我不相信你会找到它。无论您使用什么数据结构来存储初始间隔值,最坏的情况是没有一个间隔重叠,您必须检查每个间隔以确认这一点,因此 O(n)。即使有HashMap 和非常复杂的密钥结构,您仍然在查看 O(n)。

话虽如此,我不确定是否有其他方法值得研究,因为您已经找到了一种算法,可以在 O(n) 的最佳时间内解决它。

【讨论】:

  • 这当然是在区间列表排序之后。
【解决方案3】:

只是一个想法:管理一组不相交的间隔。从空集开始,添加传入间隔。如果新区间与一个或两个现有区间相交,请将它们合并。要计算交点,请使用 2 个 TreeMap,它们引用同一组区间但使用不同的键:下限和上限。

【讨论】:

    【解决方案4】:

    只是另一个想法:

    • 使用布尔数组
      • 默认将所有值设为假
      • 对于所有间隔(输入),使值为真
      • 扫描更新后的数组,得到最终的相交结果。

    【讨论】:

    • 或 java.util.BitSet 而不是布尔数组,以节省空间。
    • 这种方法(布尔数组)就像绘制区间并扫描绘制的区间以找到交点。因此,Bool 数组(二维)表示从最小值到最大值的所有可能区间(从给定的区间输入),只有输入区间设置为 true。扫描后更新的数组将给出最终的相交区间(如果有)和非相交区间——对吗?
    猜你喜欢
    • 2020-07-17
    • 2018-06-22
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    • 1970-01-01
    • 2021-06-14
    • 2015-06-26
    • 1970-01-01
    相关资源
    最近更新 更多