單調棧的原理與應用
定義:
單調棧,顧名思義,是棧內元素保持一定單調性(單調遞增或單調遞減)的棧。這裡的單調遞增或遞減是指的從棧頂到棧底單調遞增或遞減。既然是棧,就滿足後進先出的特點。與之相對應的是單調佇列。
實現:
例如實現一個單調遞增的棧,比如現在有一組數10,3,7,4,12。從左到右依次入棧,則如果棧為空或入棧元素值小於棧頂元素值,則入棧;否則,如果入棧則會破壞棧的單調性,則需要把比入棧元素小的元素全部出棧。單調遞減的棧反之。
10入棧時,棧為空,直接入棧,棧內元素為10。
3入棧時,棧頂元素10比3大,則入棧,棧內元素為10,3。
7入棧時,棧頂元素3比7小,則棧頂元素出棧,此時棧頂元素為10,比7大,則7入棧,棧內元素為10,7。
4入棧時,棧頂元素7比4大,則入棧,棧內元素為10,7,4。
12入棧時,棧頂元素4比12小,4出棧,此時棧頂元素為7,仍比12小,棧頂元素7繼續出棧,此時棧頂元素為10,仍比12小,10出棧,此時棧為空,12入棧,棧內元素為12。
至於程式碼的實現我覺得還是必須對應著題目去體會,也沒有太死板的模板,下面只給出虛擬碼吧。
/* * 本虛擬碼對應的是單調遞減棧 *共n個元素,編號為0~n-1 */ while(棧為空) 棧頂元素出棧; //先清空棧 a[n]=-1; for(i=0;i<=n;i++) { if(棧為空或入棧元素大於等於棧頂元素) 入棧; else { while(棧非空並且棧頂元素大於等於入棧元素) { 棧頂元素出棧; 更新結果; } 將最後一次出棧的棧頂元素(即當前元素可以拓展到的位置)入棧; 更新最後一次出棧的棧頂元素其對應的值; } }
輸出結果;
PS:將破壞棧單調性的元素都出棧後,最後一次出棧的元素就是當前入棧元素能拓展到的最左位置,更新其對應的值,並將其位置入棧。
應用:
以上就是一個單調棧的定義及其實現,下面就來說一下它可以解決哪些問題。其實我也不能給出證明,以證明它為什麼能完成這些功能,只是簡單的把它的用途說一下,碰到問題時就需要大家靈活運用了。
1.最基礎的應用就是給定一組數,針對每個數,尋找它和它右邊第一個比它大的數之間有多少個數。
2.給定一序列,尋找某一子序列,使得子序列中的最小值乘以子序列的長度最大。
3.給定一序列,尋找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。
對應題目:下面先只給出對應的題目和大體思路,至於題解稍後完成後再一一補充。當然這只是現階段我自己碰到過的,可能不全,還請大家多多補充指正。
應用1對應題目:
1.POJ 3250
題意:有一群牛站成一排,每頭牛都是面朝右的,每頭牛可以看到他右邊身高比他小的牛。給出每頭牛的身高,要求每頭牛能看到的牛的總數。
思路:這也就是應用1所說的求每個數和它右邊第一個比它大的數之間的數的個數,分別求出後相加即可。樸素的做法是雙重迴圈遍歷,時間複雜度為O(n^2),用單調棧為O(n)。
題解:POJ 3250題解。
應用2對應題目:
1.POJ 2559
題意:有N個矩形,寬度都為1,給出N個矩形的高度,求由這N個矩形組成的圖形包含的最大的矩形面積。
思路:可以轉化為求區間最小值乘以區間長度的最大值。普通的思路是兩重迴圈遍歷,時間複雜度為O(n^2),用單調棧為O(n)。
題解:POJ 2559 題解。
2.POJ 3494
題意:求僅由0,1組成的矩陣中,全部由1組成的小矩陣的最大面積。
思路:這個是上一題POJ 2559的升級版,把一維的操作變成二維的即可。這之前需要一個預處理。
題解:POJ 3494題解。
應用3對應題目:
1.POJ 2796
題意:給出一個序列,求出一個子序列,使得這個序列中的最小值乘以這個序列的和的值最大。
思路:直接用單調棧解決即可,由於維護單調棧的過程中會改變原陣列的值,所以需要加一個sum陣列儲存字首和,也方便計算區間元素和。
題解:POJ 2796題解。
---------------------
作者:棉花糖灬
來源:CSDN
原文:https://blog.csdn.net/zuzhiang/article/details/78134247
版權宣告:本文為博主原創文章,轉載請附上博文連結!