參考鏈接:https://github.com/itcharge/LeetCode-Py
一、簡介
單調棧(Monotone Stack):一種特殊的棧。在棧的「先進后出」規則基礎上,要求「從棧頂到棧底的元素是單調遞增(或者單調遞減)」。其中滿足從棧頂到棧底的元素是單調遞增的棧,叫做「單調遞增棧」。滿足從棧頂到棧底的元素是單調遞減的棧,叫做「單調遞減棧」。
1.1 單調遞增棧
單調遞增棧:只有比棧頂元素小的元素才能直接進棧,否則需要先將棧中比當前元素小的元素出棧,再將當前元素入棧。
這樣就保證了:棧中保留的都是比當前入棧元素大的值,并且從棧頂到棧底的元素值是單調遞增的。
單調遞增棧的入棧、出棧的過程:
- 假設當前進棧元素為 x,如果棧頂元素大于 x,則直接入棧。
- 否則從棧頂開始遍歷棧中元素,把小于 x 或者等于 x 的元素彈出棧,直到遇到一個大于 x 的元素為止,然后再把 x 壓入棧中。
實例:
-
數組元素:[2, 7, 5, 4, 6, 3, 4, 2],遍歷順序為從左到右。
image.png
最終棧中元素為 [7, 6, 4, 2]。因為從棧頂(右端)到棧底(左側)元素的順序為 2, 4, 6, 7,滿足遞增關系,所以這是一個單調遞增棧。
1.2 單調遞減棧
單調遞減棧:只有比棧頂元素大的元素才能直接進棧,否則需要先將棧中比當前元素大的元素出棧,再將當前元素入棧。
這樣就保證了:棧中保留的都是比當前入棧元素小的值,并且從棧頂到棧底的元素值是單調遞減的。
單調遞減棧的入棧、出棧的過程:
- 假設當前進棧元素為 x,如果棧頂元素大于 x,則直接入棧。
- 否則從棧頂開始遍歷棧中元素,把大于 x 或者等于 x 的元素彈出棧,直到遇到一個小于 x 的元素為止,然后再把 x 壓入棧中。
實例:
-
數組元素:[4, 3, 2, 5, 7, 4, 6, 8],遍歷順序為從左到右。
image.png
image.png
最終棧中元素為 [2, 4, 6, 8]。因為從棧頂(右端)到棧底(左側)元素的順序為 8, 6, 4, 2,滿足遞減關系,所以這是一個單調遞減棧。
二、單調棧適用場景
單調棧可以在時間復雜度為 的情況下,求解出某個元素左邊或者右邊第一個比它大或者小的元素。
所以單調棧一般用于解決一下幾種問題:
- 尋找左側第一個比當前元素大的元素
- 尋找左側第一個比當前元素小的元素
- 尋找右側第一個比當前元素大的元素
- 尋找右側第一個比當前元素小的元素
2.1 尋找左側第一個比當前元素大的元素
從左到右遍歷元素,構造單調遞增棧(從棧頂到棧底遞增):一個元素左側第一個比它大的元素就是將其「插入單調遞增棧」時的棧頂元素。如果插入時的棧為空,則說明左側不存在比當前元素大的元素。
2.2 尋找左側第一個比當前元素小的元素
從左到右遍歷元素,構造單調遞減棧(從棧頂到棧底遞減):一個元素左側第一個比它小的元素就是將其「插入單調遞減棧」時的棧頂元素。如果插入時的棧為空,則說明左側不存在比當前元素小的元素。
2.3 尋找右側第一個比當前元素大的元素
從左到右遍歷元素,構造單調遞增棧(從棧頂到棧底遞增):一個元素右側第一個比它大的元素就是將其「彈出單調遞增棧」時即將插入的元素。如果該元素沒有被彈出棧,則說明右側不存在比當前元素大的元素。
從右到左遍歷元素,構造單調遞增棧(從棧頂到棧底遞增):一個元素左側第一個比它大的元素就是將其「插入單調遞增棧」時的棧頂元素。如果插入時的棧為空,則說明左側不存在比當前元素大的元素。
2.4 尋找右側第一個比當前元素小的元素
從左到右遍歷元素,構造單調遞減棧(從棧頂到棧底遞減):一個元素右側第一個比它小的元素就是將其「彈出單調遞減棧」時即將插入的元素。如果該元素沒有被彈出棧,則說明右側不存在比當前元素小的元素。
從右到左遍歷元素,構造單調遞減棧(從棧頂到棧底遞減):一個元素右側第一個比它小的元素就是將其「插入單調遞減棧」時的棧頂元素。如果插入時的棧為空,則說明右側不存在比當前元素小的元素。
小結
- 無論哪種題型,都建議從左到右遍歷元素。
- 查找 「比當前元素大的元素」 就用 單調遞增棧,查找「比當前元素小的元素」就用 單調遞減棧。
- 從 「左側」 查找就看 「插入棧」 時的棧頂元素,從 「右側」 查找就看 「彈出棧」 時即將插入的元素。
三、單調棧模板
3.1 單調遞增棧模板
def monotoneIncreasingStack(nums):
stack = []
for num in nums:
while stack and num >= stack[-1]:
stack.pop()
stack.append(num)
3.2 單調遞減棧模板
def monotoneDecreasingStack(nums):
stack = []
for num in nums:
while stack and num <= stack[-1]:
stack.pop()
stack.append(num)
四、單調棧的應用
下一個更大元素、每日溫度等。
五、總結
本章節主要介紹了單調棧的類型、基礎理論、實現過程和使用場景。單調棧原理很簡單,就是按大小排成一列,但是不能小覷,它的操作不能一步到位,只能一步一步實現,而且不一定所有的數都能入棧,有些數可能被彈出去不能入棧。