簡單的單調棧演算法

2022-02-08 23:13:40 字數 2465 閱讀 1491

目錄應用

單調棧是一種演算法, 它可以用一次掃瞄 \(o(n)\) 時間求出序列中每個數向左和向右的第乙個大於它的數, 也可以用一次掃瞄 \(o(n)\) 時間求出序列中每個數向左和向右的第乙個小於它的數。

以大於為例:

用單調棧求左邊第乙個大於的數:

有數列\(\big\)。

對於乙個數 \(a_i\) 前面的兩個數 \(a_p,a_q, \quad p, 若 \(a_p, 則對於所有 \(j\ge i\), \(a_p\) 都不可能成為 \(a_j\) 的答案。

單調棧就是對於每個數維護可能成為其答案的下標集合, 然後從裡面快速選出答案。

具體地, 演算法流程是:

一、 從左到右掃瞄數列, 開乙個空棧 \(stk\), 棧頂為 \(stk[tp]\)

二、對於每個數 \(a_i\),

在棧不為空且 \(stk[tp] \le a_i\) 的時候使棧不斷 \(pop\)

停止 \(pop\) 後, 若棧不為空, \(stk[tp]\) 就是 \(a_i\) 左邊第乙個大於其的數, 否則 \(a_i\) 左邊沒有大於其的數。

將 \(a_i\) 加入到棧頂

從以上演算法可以看出, 每個數 \(a_i\) 都是會被加入棧裡的, 那麼在以後的掃瞄過程中, 讓 \(a_i\) 從棧中彈出的那個數一定就是 \(a_i\) 右邊第乙個大於其的數, 若自始至終 \(a_i\) 沒有被彈出, 則 \(a_i\) 右邊沒有大於其的數。

至此, 可以用單調棧一次掃瞄求出每個數左邊和右邊第乙個大於其的數, 對於小於, 只需簡單地魔改一下演算法就可以了, 用到的理論也是高度類似的

需要證明的僅僅是求左邊第乙個大於的數的正確性, 懂的都懂。

那麼對於每個數 \(a_i\), 在演算法流程掃瞄到 \(a_i\), 執行到二-2的時候, 為什麼能直接確定答案?

首先可以確定棧中沒有不可能成為當前數的答案的數 (當然也沒有不可能成為以後數的答案的數), 此時選棧頂, 即最靠近當前數的數, 就是答案。

假設掃瞄到 \(a_i\),

用歸納法證明到步驟二-1之前,棧中沒有不可能成為 \(a_i\) 答案的數, 所有可能成為 \(a_i\) 答案的數都在棧中。

用歸納法證明到步驟二-1之前,從棧底到棧頂是乙個單調遞減的數列, 這就可以證明執行完步驟二-1之後棧頂就是答案

可以用歸納法證明, 對於數列 \(\big\), 在使用基本演算法求每個數的左邊的第乙個大於其的數時, 對於當前掃瞄的數 \(a_i\), 單調棧實際上是維護的數列 \(\big_\) 的字尾最大值(從棧頂到棧底, 就是 \(max(a[i\cdots i])、max(a[i-1\cdots i])、\cdots、max(a[1\cdots i])\) 去重後的結果)。

於是在單調棧的過程中, 可以見到整個數列所有子串的最大值, 至於有多少應用嘛……不知道。

求 \(\sum_^n\sum_^n max(a_)\)。

根據上面的更深一點的應用單調棧 \(o(n)\) 搞, 很不錯。

還可以列舉所有 \(a_i\), 算出 max 是 \(a_i\) 的區間有多少個, 這個也可以用單調棧求出來。

\(f(x,y) = \sum_^y\sum_^y max(a_)\)

\(q\) 次詢問不同的 \(f(x,y)\)

要求 \(o((n+q)\log n)\)。

似乎不太可做, 待補。

給定數列 \(a\) , 求 \(max\*(a_r-a_l)\) 的最大值。

列舉 \(max\\), 再討論一下, 就做完了。(用 st表 可以做到 \(o(n\log n)\))

如果把 \(a_r-a_l\) 換成 \(r-l+1\), 可以做到 \(o(n)\)。

給定數列 \(a\), 求有幾對 \((x,y)\) 滿足:

\(max(a[x+1\cdots y-1]) \le min(a[x],a[y])\)。

順便可以求出具體方案。

單調棧求左邊第乙個大於的數的過程中, 被乙個數彈掉的所有數以及此數的答案, 就是所有可以使這個數作為 \(y\) 的 \(x\)。

這樣的對數是 \(o(n)\) 的。

順便如果把題目中式子的小於等於號改成小於號, 此演算法也能勝任, 而且針對小於號還有一種其它的演算法:

所有 \((x,y)\) 都是某乙個數的 \((左邊第乙個大於的,右邊第乙個大於的)\)。

給定矩陣, 求面積最大的和 \(>0\) 的子矩陣。

首先列舉上下邊界壓縮一維(就像求最大子矩陣和一樣)。

然後問題就變成了在乙個數列中, 對於每個數求出最左邊的小於其的數。

當然這並不是單調棧, 但是比較像單調棧。(其實是維護了字首最小值)

總複雜度 \(o(n^3\log n)\)

演算法 單調棧

單調棧 從左往右進行遍歷,如果棧頂元素大於等於當前的元素,直接出棧 while 迴圈 退出while 迴圈兩個條件,棧空,或者棧不為空,此時棧頂元素小於單前元素 if 如果棧不為空,表明棧頂元素小於當前元素,將棧頂元素儲存在當前元素對應下標的陣列中 從右邊向左進行遍歷 while 如果棧頂元素大於當...

單調棧演算法筆記

定義 單調棧就是棧內元素遞增或者單調遞減的棧,並且只能在棧頂操作。單調棧的維護是o n 的時間複雜度,所有元素只會進進棧一次 性質 單調棧裡面的元素具有單調性 元素加入棧前會把棧頂破壞單調性的元素刪除 使用單調棧可以找到元素向左遍歷的第乙個比他小的元素 單增棧 也可以找到元素向左遍歷第乙個比他大的元...

演算法 單調棧專題

單調棧是一種理解起來很容易,但是運用起來並不那麼簡單的資料結構。一句話解釋單調棧,就是乙個棧,裡面的元素的大小按照他們所在棧內的位置,滿足一定的單調性。題目是這樣的,給乙個陣列,返回乙個大小相同的陣列。返回的陣列的第i個位置的值應當是,對於原陣列中的第i個元素,至少往右走多少步,才能遇到乙個比自己大...