← Все вопросы

Минимум на скользящем окне за O(n) через монотонную очередь (дек) — как это работает?

Задан 23 месяца назад893 просмотров2 ответа
11

Нужно для каждого окна длины k найти минимум по массиву. multiset даёт O(n log k), но в разборе пишут O(n) через «монотонный дек». Не понимаю, почему элементы можно выкидывать и почему это в сумме линейно.

2 ответа

17
✓ Принятый ответ — помог автору

Идея монотонной очереди: храним в деке индексы элементов-кандидатов на минимум так, чтобы значения шли строго по возрастанию от головы к хвосту. Тогда минимум окна всегда в голове дека.

При добавлении нового элемента a[r] выкидываем с хвоста все элементы >= a[r]: они уже никогда не станут минимумом — новый элемент и меньше, и «свежее» (дольше проживёт в окне). При сдвиге окна выкидываем с головы индекс, вышедший за левую границу (<= r - k).

vector<int> slidingMin(const vector<int>& a, int k) {
    deque<int> dq; // индексы, значения возрастают от головы к хвосту
    vector<int> res;
    for (int r = 0; r < (int)a.size(); r++) {
        while (!dq.empty() && a[dq.back()] >= a[r]) dq.pop_back();
        dq.push_back(r);
        if (dq.front() <= r - k) dq.pop_front();
        if (r >= k - 1) res.push_back(a[dq.front()]);
    }
    return res;
}

Сложность O(n) время, O(k) память.

Почему линейно (амортизация): каждый индекс добавляется в дек ровно один раз (push_back) и удаляется максимум один раз (pop_back или pop_front). То есть суммарное число операций над деком ≤ 2n, несмотря на то что внутренний while иногда удаляет много элементов за одну итерацию. Это классический амортизированный анализ методом «каждый элемент платит за своё удаление». Для максимума — поменяй >= на <=.

6

Пара практических нюансов:

  1. Храни именно индексы, а не значения — иначе не отличишь «вышел за окно» и не удалишь дубликаты значений корректно.
  2. Порядок операций важен: сначала чистим хвост по значению, потом push, потом чистим голову по границе, потом снимаем ответ. Если переставить чистку головы — можно выдать минимум окна, в котором голова уже невалидна.
  3. Эта же монотонная очередь — основа оптимизации динамики вида dp[i] = min(dp[j]) + cost по скользящему диапазону j: убирает множитель окна из сложности, превращая O(nk) в O(n).

Ваш ответ

Войдите, чтобы ответить на вопрос.