Monotoni stek i red: sledeći veći i minimum prozora

Lekcija 3 od 7 · Napredne tehnike

Stek ili red u kome su elementi uvek poređani po veličini (monotono) rešava u O(n) zadatke koji naivno traže O(n²): „prvi veći element desno“, „najveći pravougaonik u histogramu“, „minimum svakog prozora dužine k“.

Pre nego što počneš: potrebno predznanje – stek, red, dva pokazivača.
Kako da učiš: svaki primer prekucaj i pokreni, a zadatke na kraju prvo reši sam/a; uputstvo i rešenje otvori tek posle toga. Svi programi su u C++-u.

Sledeći veći element

Za svaki element niza treba naći prvi element desno od njega koji je veći (ili −1). Idemo sleva nadesno i na steku držimo pozicije elemenata kojima još nije nađen veći; njihove vrednosti su na steku opadajuće. Kada stigne novi element a[i], on je „sledeći veći“ za sve elemente sa vrha steka koji su manji od njega – njih skidamo.

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n), odg(n, -1);
    for (int &x : a) cin >> x;
    stack<int> st;                         // pozicije bez nađenog većeg; vrednosti opadaju
    for (int i = 0; i < n; i++) {
        while (!st.empty() && a[st.top()] < a[i]) {
            odg[st.top()] = a[i];          // a[i] je prvi veći za element na vrhu
            st.pop();
        }
        st.push(i);
    }
    for (int i = 0; i < n; i++) cout << odg[i] << (i + 1 < n ? " " : "\n");
    return 0;
}

Svaki element jednom uđe na stek i najviše jednom izađe, pa je ukupno O(n), iako postoji unutrašnja while petlja.

Najveći pravougaonik u histogramu

Dati su stubovi visina h[0..n−1] širine 1. Najveći pravougaonik koji staje u histogram ima visinu nekog stuba h[i] i proteže se levo i desno dok su stubovi bar te visine. Monotonim stekom u jednom prolazu nađemo, za svaki stub, prvi niži stub levo i desno.

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<long long> h(n);
    for (auto &x : h) cin >> x;
    vector<int> levo(n), desno(n);
    stack<int> st;
    for (int i = 0; i < n; i++) {                 // prvi niži levo
        while (!st.empty() && h[st.top()] >= h[i]) st.pop();
        levo[i] = st.empty() ? -1 : st.top();
        st.push(i);
    }
    while (!st.empty()) st.pop();
    for (int i = n - 1; i >= 0; i--) {            // prvi niži desno
        while (!st.empty() && h[st.top()] >= h[i]) st.pop();
        desno[i] = st.empty() ? n : st.top();
        st.push(i);
    }
    long long naj = 0;
    for (int i = 0; i < n; i++)
        naj = max(naj, h[i] * (desno[i] - levo[i] - 1));   // širina između nižih suseda
    cout << naj << endl;
    return 0;
}

Minimum svakog prozora: monotoni red (deque)

Za svaki uzastopni deo („prozor“) dužine k treba ispisati najmanji element. U redu sa dva kraja (deque) držimo pozicije čije su vrednosti rastuće: na početku je minimum prozora. Novi element izbacuje sa kraja sve veće od sebe (oni više nikad neće biti minimum), a sa početka izbacujemo pozicije koje su izašle iz prozora.

#include <iostream>
#include <vector>
#include <deque>
using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    for (int &x : a) cin >> x;
    deque<int> dq;                                // pozicije; vrednosti rastu od početka ka kraju
    for (int i = 0; i < n; i++) {
        while (!dq.empty() && a[dq.back()] >= a[i]) dq.pop_back();   // veći više nisu kandidati
        dq.push_back(i);
        if (dq.front() <= i - k) dq.pop_front();  // izašao iz prozora [i-k+1, i]
        if (i >= k - 1) cout << a[dq.front()] << (i + 1 < n ? " " : "\n");
    }
    return 0;
}

I ovde svaki element jednom uđe i jednom izađe – O(n). Za maksimum prozora samo se obrne poređenje.

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje. Rešenja su provereno tačna na primerima iz teksta.

ZADATAK 01Težina: Srednje

Koliko dana je cena bila niža?

Za svaki dan data je cena akcije. Za svaki dan ispiši koliko je uzastopnih dana zaključno sa tim danom cena bila manja ili jednaka današnjoj (uključujući i današnji dan).

Primer

Ulaz

7
100 80 60 70 60 75 85

Izlaz

1 1 1 2 1 4 6
ZADATAK 02Težina: Lako

Najtopliji period

Data su merenja temperature za n dana i broj k. Za svaki period od k uzastopnih dana ispiši najvišu temperaturu.

Primer

Ulaz

6 2
3 1 4 1 5 9

Izlaz

3 4 4 5 9
Česte greške i saveti
  • Na stek/red stavljaj pozicije, ne vrednosti – tako znaš i koliko je element daleko i da li je izašao iz prozora.
  • Pre st.top() uvek proveri da stek nije prazan.
  • Odluči da li uslov skidanja treba da bude < ili <= – razlika je u tome kako se tretiraju jednaki elementi.

Proveri znanje

Otvori kviz

Monotoni stek i red

1. Koja je složenost nalaženja sledećeg većeg elementa monotonim stekom?




2. Šta je na početku monotonog reda za minimum prozora?




3. Koja C++ struktura dozvoljava dodavanje i brisanje sa oba kraja?