1. 程式人生 > 其它 >【html-css】02 html的基本結構

【html-css】02 html的基本結構

單調棧

什麼是單調棧

顧名思義,如果棧中元素單調增(或減),則該棧被稱為單調棧

如何維護單調棧

以維護一個單調增的棧為例, 插入元素2, 1, 4, 5, 3。

  • 插入元素2,此時棧中為空,直接入棧;棧中元素:2。

  • 插入元素1,棧頂元素2大於1,先出棧,此時棧中為空,插入1;棧中元素:1。

  • 插入元素4,棧頂元素1小於4,插入4;棧中元素:1,4。

  • 插入元素5,棧頂元素4小於5,插入5;棧中元素:1,4,5。

  • 插入元素3,棧頂元素5大於3,先出棧,此時棧頂元素4大於3,再次出棧,最後棧頂元素1小於3,入棧,棧中元素:1,3。

這樣我們就維護了一個單調增的棧了。

核心程式碼

stack<int>sta;
void insert(x){
    while(!sta.empty() && sta.top() >= x)sta.pop();
    sta.push(x);
}

應用

洛谷 P5788【模板】單調棧

題目大意

給定一個數列\(\,a_{i...n}\),找到每一個數向右第一個大於它的數的下標,即\(\,a_i<a_j,i<j,\text {求每一個}\,a_i\text{對應的}\text {min}(j)\),沒有則為0。\((1\le n \le 3\times10^6)\)

題目思路

如果我們直接暴力列舉,對每一個數\(\,a_i\,\)往右進行一次查詢。這種做法的時間複雜度最壞的情況下是 \(O(n^2)\) 的,這無疑是會TLE的。下面考慮優化。

首先觀察一個特殊情況:5,3,3,2,1。顯然每個數都沒有滿足題意的下標,這時如果我們在其右邊在插入一個大小為3的數,則可以模擬以下情況:

  • 首先1和2小於3,則都有其對應答案。
  • 接下來由於原來數列本身是不嚴格遞減的,所以3及以後的數必定大於等於3,所以後面的數依舊沒有答案。(這裡的不嚴格遞減指的是數列中\(\,a_i\ge a_{i - 1}, \forall\ i \in[1,n)\) 都成立。
  • 因為1和2都有答案了,直接退出數列就好,這時,我們可以得到下一個數列:5,3,3,3。我們可以發現,得到的數列依然是一個不嚴格遞減數列。重複上述步驟便可以得出每一個數答案了

經過上面的模擬過程,相信你應該知道為什麼要用單調棧了,我們不妨維護一個不嚴格遞減的單調棧,每次要出棧時記錄答案即可,像上面的1和2一樣。

這便是單調棧的一個簡單應用

程式碼

#include<cstdio>
#include<stack>

int a[3000005]; //記錄答案

int main(void){
    int n;
    std::stack<int>sta; //記錄資料大小
    std::stack<int>ind; //記錄對應下標
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int key;
        scanf("%d",&key);
        while(!sta.empty()&&sta.top()<key){ //單調棧核心程式碼
            a[ind.top()]=i; //記錄大於該數的下標
            sta.pop();
            ind.pop();
        }
    sta.push(key);
    ind.push(i);
    }
    for(int i=1;i<=n;i++)printf("%d ",a[i]);
    return 0;
}