單調棧之Next Greater Number

2021-10-03 12:45:44 字數 3345 閱讀 5509

棧(stack) 是很簡單的⼀種資料結構, 先進後出的邏輯順序, 符合某些問

題的特點, ⽐如說函式調⽤棧。

單調棧實際上就是棧, 只是利⽤了⼀些巧妙的邏輯,使得每次新元素⼊棧

後, 棧內的元素都保持有序(單調遞增或單調遞減) 。

聽起來有點像堆(heap) ? 不是的, 單調棧⽤途不太⼴泛, 只處理⼀種典型

的問題, 叫做next greater element

⾸先, 講解next greater number的原始問題:給你⼀個陣列, 返回⼀個等

⻓的陣列, 對應索引儲存著下⼀個更⼤元素, 如果沒有更⼤的元素, 就存-1。 不好⽤語⾔解釋清楚, 直接上⼀個例⼦:

給你⼀個陣列 [2,1,2,4,3], 你返回陣列 [4,2,4,-1,-1]。

解釋: 第⼀個 2 後⾯⽐ 2 ⼤的數是 4; 1 後⾯⽐ 1 ⼤的數是 2; 第⼆個 2 後⾯

⽐ 2 ⼤的數是 4; 4 後⾯沒有⽐ 4 ⼤的數, 填 -1; 3 後⾯沒有⽐ 3 ⼤的數, 填

-1。

這道題的暴⼒解法很好想到, 就是對每個元素後⾯都進⾏掃瞄, 找到第⼀個

更⼤的元素就⾏了。 但是暴⼒解法的時間複雜度是 o(n

2)o(n^2)

o(n2)。

這個問題可以這樣抽象思考:把陣列的元素想象成並列站⽴的⼈, 元素⼤⼩想象成⼈的⾝⾼。 這些⼈⾯對你站成⼀列, 如何求元素「2」 的 next greater number 呢? 很簡單, 如果能夠看到元素「2」 , 那麼他後⾯可⻅的第⼀個⼈就是「2」 的 next greater number, 因為⽐「2」 ⼩的元素⾝⾼不夠, 都被「2」 擋住了, 第⼀個露出來的就是答案

這個情景很好理解吧? 帶著這個抽象的情景, 先來看下**。

vector<

int>

nextgreaterelement

(vector<

int>

& nums)

ans[i]

= s.

empty()

?-1: s.

top();

// 這個元素⾝後的第⼀個⾼個

s.push

(nums[i]);

// 進隊, 接受之後的⾝⾼判定吧!

}return ans;

}

這就是單調佇列解決問題的模板。 for 迴圈要從後往前掃瞄元素, 因為我們借助的是棧的結構, 倒著⼊棧, 其實是正著出棧。while 迴圈是把兩個「⾼個」元素之間的元素排除, 因為他們的存在沒有意義, 前⾯擋著個「更⾼」的元素, 所以他們不可能被作為後續進來的元素的 next great number 了.

這個演算法的時間複雜度不是那麼直觀, 如果你看到 for 迴圈巢狀 while 迴圈, 可能認為這個演算法的複雜度也是 o(n

2)o(n^2)

o(n2

), 但是實際上這個演算法的複雜度只有o(n

)o(n)

o(n)

分析它的時間複雜度, 要從整體來看: 總共有 n 個元素, 每個元素都被push ⼊棧了⼀次, ⽽最多會被 pop ⼀次, 沒有任何冗餘操作。 所以總的計算規模是和元素規模 n 成正⽐的, 也就是 o(n

)o(n)

o(n)

複雜度

你已經掌握了單調棧的使⽤技巧, 來⼀個簡單的變形來加深⼀下理解:

給你⼀個陣列 t = [73, 74, 75, 71, 69, 72, 76, 73], 這個陣列存放的是近⼏天的天⽓⽓溫(這⽓溫是鐵板燒? 不是的, 這⾥⽤的華⽒度) 。 你返回⼀個陣列, 計算: 對於每⼀天, 你還要⾄少等多少天才能等到⼀個更暖和的⽓溫;如果等不到那⼀天, 填 0 。

舉例: 給你 t = [73, 74, 75, 71, 69, 72, 76, 73], 你返回 [1, 1, 4, 2, 1, 1, 0, 0]。

解釋: 第⼀天 73 華⽒度, 第⼆天 74 華⽒度, ⽐ 73 ⼤, 所以對於第⼀天,

只要等⼀天就能等到⼀個更暖和的⽓溫。 後⾯的同理

相同型別的問題, 相同的思路, 直接調⽤單調棧的演算法模板, 稍作改動就可以啦, 直接上**吧

vector<

int>

dailytemperatures

(vector<

int>

& t)

ans[i]

= s.

empty()

?0:(s.

top(

)- i)

;// 得到索引間距

s.push

(i);

// 加⼊索引, ⽽不是元素

}return ans;

}

同樣是 next greater number, 現在假設給你的陣列是個環形的, 如何處理?

給你⼀個陣列 [2,1,2,4,3], 你返回陣列 [4,2,4,-1,4]。 擁有了環形屬性, 最後

⼀個元素 3 繞了⼀圈後找到了⽐⾃⼰⼤的元素 4 。

next greater number 的問題, 增加了環形屬性後, 問題的難點在於:這個 next 的意義不僅僅是當前元素的右邊了, 有可能出現在當前元素的左邊.

我們可以考慮這樣的思路將原始陣列「翻倍」, 就是在後⾯再接⼀個原始陣列, 這樣的話, 按照之前「⽐⾝⾼」的流程, 每個元素不僅可以⽐較⾃⼰右邊的元素, ⽽且也可以和左邊的元素⽐較了。

怎麼實現呢? 你當然可以把這個雙倍⻓度的陣列構造出來,然後套⽤演算法模板。 但是, 我們可以不⽤構造新陣列, ⽽是利⽤迴圈陣列的技巧來模擬。 直接看**吧:

vector<

int>

nextgreaterelements

(vector<

int>

& nums)

return res;

}

演算法之單調棧與單調佇列

單調佇列顧名思義就是具有單一單調性的佇列。給定乙個數列,從左至右輸出每個長度為m的數列段內的最小數和最大數。數列長度 n 106,m n 數列為 6 4 10 10 8 6 4 2 12 14,求長度為3的數列段內的最大數,使用單調遞減棧。1 6,0 入隊,此時隊列為 6,0 2 4,1 入隊,此時...

單調棧 模板 單調棧模板

biu 單調棧主要用於求取左邊第乙個比它大,或者比它小的數。就比如站隊隨便排成一列,可以求到每個人後面第乙個比他高的人。同理可以推廣至右邊,比它矮均可。這就是單調遞增棧 遞減棧,從前至 棧,從後向前入棧的區別了。單調棧比較抽象,非常具有智慧型的想法,可應用的場景相當少,根據幾個經典題目體會它的用法會...

單調佇列 單調棧

參考文章 單調佇列 poj 2823 給定乙個數列,從左至右輸出每個長度為m的數列段內的最小數和最大數。數列長度 n 106 m n n 106,m n n 106 m n 直接暴力求解複雜度在0 mn 可以考慮維護區間最值,單調佇列則是維護區間佇列的強大 單調佇列的定義 單調佇列實現的大致過程 1...