Поиск элементарных интервалов в перекрывающихся интервалах
я наткнулся на хороший вопрос при подготовке к интервью Программирование.
учитывая набор возможно перекрывающихся интервалов, вам нужно написать функцию, чтобы вернуть все элементарные интервалы между ними. Например: если вам заданы интервалы в виде следующего списка пар: {{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", определенной в {3,10} в вход, который разрезает интервал на два элементарных интервала.
подпись метода в Java:
List<Pair<Integer, Integer>> generateElementaryIntervals(List<Pair<Integer, Integer> intervals)
одним из решений, которые я себе представлял, было разделение ввод в непересекающиеся множества, а затем простая сортировка O(NlogN) по всем числам в каждом непересекающемся множестве даст ответ. Существует ли более эффективный способ сделать это?
3 ответов
вы можете сначала разбить эту проблему на вложенные интервалы, а затем заниматься каждым вложением отдельно. Под вложенными я подразумеваю интервалы, которые разделяют хотя бы одну точку. Для пример:
{{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: (Обратите внимание, что я использую 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());
}
}
простым алгоритмом было бы просто прочитать весь список чисел и создать элемент для каждого элемента в каждой паре.
каждый элемент будет хранить два значения:number
, и является ли это первым или вторым номером (от входа).
эти пары затем будут отсортированы, сначала по его внутреннему number
, а затем по его положению (second
пошел бы раньше first
)
распечатать список интервалами, печатается каждое число вместе со следующим номером, со следующими правилами:
- вы не будете печатать повторяющиеся числа (например, вы не будете печатать 5,5)
- если у вас есть только
second
количество, а затемfirst
number, вы не будете печатать этот элементарный интервал, так как в этом диапазоне нет значений.