Непересекающиеся интервалы

Средняя

Описание задачи #

Дан массив интервалов intervals, где intervals[i] = [starti, endi]. Напишите функцию, которая возвращает минимальное количество интервалов, которое вам нужно удалить, чтобы остальные интервалы не пересекались.


Ограничения #


Примеры #

  • Входные данные: [[1,2],[2,3],[3,4],[1,3]]

    Ответ: 1

    Интервал [1,3] можно удалить, а остальные не перекрываются.

  • Входные данные: [[1,2],[1,2],[1,2]]

    Ответ: 2

    Нужно удалить два интервала [1,2], чтобы остался один не пересекающийся.

  • Входные данные: [[1,2],[2,3]]

    Ответ: 0

    Не нужно удалять какие-либо интервалы, поскольку они уже не перекрываются.


Решение #

Рассмотрим два интервала с самым ранним временем окончания. Допустим, более раннее время окончания — x, а более позднее — y, то есть х < у.

Если мы можем сохранить только один интервал, следует ли нам выбрать тот, который заканчивается на x или тот, который оканчивается на y? Чтобы избежать дублирования, мы всегда должны жадно выбирать интервал с более ранним временем окончания x. Логику, стоящую за этим, можно описать следующим образом:

В общем, k равно времени окончания самого последнего сохраненного нами интервала.

Решение задачи мы начинаем с сортировки интервалов по времени окончания, чтобы мы могли обрабатывать интервалы по порядку.

Поскольку мы отсортировали интервалы по времени окончания, y должно быть больше k. Это дает нам два возможных варианта:

Реализация #

  • package main
    
    import "sort"
    
    func eraseOverlapIntervals(intervals [][]int) int {
    	sort.Slice(intervals, func(i, j int) bool {
    		return intervals[i][1] < intervals[j][1]
    	})
    
    	res := 0
    	k := intervals[0][1]
    
    	for idx := 1; idx < len(intervals); idx++ {
    		end := intervals[idx][1]
    		start := intervals[idx][0]
    
    		if start >= k {
    			k = end
    		} else {
    			res++
    		}
    	}
    
    	return res
    }

  • export const eraseOverlapIntervals = (intervals: number[][]): number => {
        intervals.sort((a, b) => {
            return a[1] - b[1]
        })
    
        let res = 0
        let k = intervals[0][1]
    
        for (let i = 1; i < intervals.length; i++) {
            const end = intervals[i][1]
            const start = intervals[i][0]
    
            if (start >= k) {
                k = end
            } else {
                res++
            }
        }
    
        return res
    }

Оценка сложности #

По времени

Для того чтобы найти ответ, нам достаточно один раз пройтись по исходному массиву со сложностью O(n). Однако предварительно мы еще делаем сортировку массива, сложность которой скорее всего равна O(n⋅logn).

Поэтому итоговая сложность по времени равна O(n⋅logn).

По памяти

Мы не создаем переменных, зависящих от длины входных данных, однако для сортировки также требуется выделение памяти. В разных языках используются разные алгоритмы, поэтому сложность по памяти будет варьироваться от O(logn) до O(n).

Оригинал задачи на Leetcode
< Предыдущая задача
Следующая задача >