Монотонная очередь

Базовый Algorithms
≈ 2 мин чтения

Кандидаты для текущего окна

Монотонная очередь обычно хранится в deque и поддерживает кандидатов в отсортированном по значению порядке. Новые элементы добавляются с одного конца, а бесполезные удаляются с другого. В отличие от обычной очереди, часть элементов может исчезнуть ещё до того, как дошла бы до головы: если новый кандидат гарантированно лучше старого.

Максимум в каждом окне

Пусть для каждого окна длины k нужен максимум. В deque будем хранить индексы так, чтобы соответствующие значения шли по убыванию. Голова тогда всегда указывает на максимум текущего окна.

static int[] maxInWindows(int[] numbers, int k) {
    int[] result = new int[numbers.length - k + 1];
    Deque<Integer> deque = new ArrayDeque<>();

    for (int i = 0; i < numbers.length; i++) {
        while (!deque.isEmpty() && deque.peekFirst() <= i - k) {
            deque.removeFirst();
        }
        while (!deque.isEmpty() && numbers[deque.peekLast()] <= numbers[i]) {
            deque.removeLast();
        }
        deque.addLast(i);

        if (i >= k - 1) {
            result[i - k + 1] = numbers[deque.peekFirst()];
        }
    }

    return result;
}

Два разных повода удалить элемент

Индекс удаляется с головы, когда он вышел за левую границу окна. С хвоста элементы удаляются по другой причине: новое значение не меньше их, поэтому пока оно остаётся в окне, старые кандидаты уже не смогут стать максимумом.

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