Объединение пересекающихся интервалов

Возможный дубликат:
объединение интервалов
как эффективно перекрывать интервалы

Учитывая список интервалов, в которых указаны все целые числа, должна быть возможность свернуть их в один интервал, если они действительно пересекаются или перекрываются, в противном случае данные интервалы остаются неизменными. Скажем, если вход, например, 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 и high = 6 // Шаг 1: Сортировка по нижней конечной точке данного интервалы // Шаг 2: найти объединение отсортированных интервалов

//Вход:

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;
      }

}

person sangv    schedule 10.09.2012    source источник
comment
Мой опыт работы с java не самый лучший, но ваш из-за docs.oracle.com/javase/1.4.2/docs/api/java/util/, java.util.Comparator) вы не написали алгоритм, который работает в O (n), но вместо этого O (n * log n)   -  person citronas    schedule 10.09.2012
comment
-1 за то, что не исследовал и не обращал внимания на предлагаемые дубликаты, предоставленные SO при вводе этого вопроса. На это несколько раз ответили на SO   -  person Jim Garrison    schedule 10.09.2012
comment
можно решить за O (n), но наибольшее число в интервале должно быть сопоставимо с n.   -  person Akashdeep Saluja    schedule 10.09.2012
comment
@AkashdeepSaluja: Может быть решена за O (n), если выбрана сортировка без сравнения, такая как сортировка с подсчетом, где помогает знание наибольшего числа во входных данных?   -  person sangv    schedule 11.09.2012
comment
@sangv да, можно сказать, что идея в основном состоит в сортировке по счетчику, но не совсем. Также я опубликовал свое предложение в качестве ответа.   -  person Akashdeep Saluja    schedule 11.09.2012


Ответы (4)


Возьмите массив размера n (где n - наибольшее число) и заполните начало и конец интервала значениями 1 и -1 соответственно.

Под этим я подразумеваю, что если интервалы

{[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 и добавьте значение, хранящееся в местоположении массива, к сумме.

Указание суммы по каждому индексу массива:

В ячейке 1: сумма = 1 (1 в индексе 1)

В ячейке 2: сумма = 2 (1 в индексе 2)

В ячейке 3: сумма = 2 (0 в индексе 3)

В позиции 4: сумма = 1 (-1 в индексе 4)

В ячейке 5: сумма = 0 (-1 в индексе 5)

Теперь сумма достигает нуля, это означает, что на этом интервал заканчивается, поэтому новый интервал будет [1-5]

В ячейке 6: сумма = 0 (0 в индексе 6)

В ячейке 7: сумма = 1 (1 в индексе 7)

(В ячейке 7 сумма снова становится больше нуля, это означает, что интервал только что начался)

В ячейке 8: сумма = 1 (0 в индексе 8)

В ячейке 9: сумма = 0 (-1 в индексе 9)

Интервал, начатый в позиции 7, только что закончился, поэтому новые диапазоны интервалов будут

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

Надеюсь, это поможет.

person Akashdeep Saluja    schedule 11.09.2012
comment
К вашему сведению, это не работает с тестовым примером (например, если у вас есть дубли): (-1, -1), (-1, -1), (3, 5), (6, 6), (3 , 5) - person kenyee; 12.09.2013

Если вы ищете для этой проблемы более эффективный алгоритм, чем O (n), я не верю, что вы его найдете. Независимо от того, какую структуру данных вы используете для хранения начальных значений интервалов, ваш наихудший сценарий состоит в том, что ни один из интервалов не перекрывается, и вам нужно проверять каждый интервал, чтобы подтвердить это, следовательно, O (n). Даже с HashMap и очень сложной ключевой структурой вы все равно смотрите на O (n).

При этом я не уверен, стоит ли исследовать какой-либо другой подход, поскольку вы уже нашли алгоритм, который решает его в наилучшее возможное время, O (n).

person Jordan Ell    schedule 10.09.2012
comment
Это, конечно, после того, как список интервалов отсортирован. - person Jordan Ell; 10.09.2012

Просто идея: управлять набором непересекающихся интервалов. Начиная с пустого набора, добавляем входящие интервалы. Если новый интервал пересекается с одним или двумя существующими интервалами, объедините их. Для расчета пересечений используйте 2 карты дерева, ссылающиеся на один и тот же набор интервалов, но с разными ключами: нижней и верхней границей.

person Alexei Kaigorodov    schedule 10.09.2012

Еще одна идея:

  • Use a Bool array
    • make all the values false by default
    • для всех интервалов (на входе) сделать значения истинными
    • Отсканируйте обновленный массив и получите окончательный результат пересечения.
person k53sc    schedule 10.09.2012
comment
или java.util.BitSet вместо логического массива для экономии места. - person Alexei Kaigorodov; 10.09.2012
comment
Этот подход (массив Bool) похож на построение интервалов и сканирование нанесенного интервала для поиска пересечения. Итак, массив Bool (двумерный) представляет все возможные интервалы от минимального до максимального значения (от заданного ввода интервалов), причем только для интервалов ввода установлено значение true. Этот обновленный массив при сканировании даст окончательный интервал пересечения (если есть) и непересекающиеся интервалы - верно? - person sangv; 11.09.2012