理论
单调栈是一个维护性的数据结构,通常用于一下场景:
给定数组nums, 对于某一位置i :
找到向左数第一个比nums[i]大 的位置 —> 从左向右维护一个单调递减 栈;
找到向左数第一个比nums[i]小 的位置 —> 从左向右维护一个单调递增 栈;
找到向右数第一个比nums[i]大 的位置 —> 从右向左维护一个单调递减 栈;
找到向右数第一个比nums[i]小 的位置 —> 从右向左维护一个单调递增 栈。
从栈中元素来看,递减栈会剔除波谷,留下波峰;递增栈剔除波峰,留下波谷。
栈是否是严格递增或者严格递减,与nums中元素是否有重复无关,取决于栈中是否可以存在相同的元素。
例题
Case 1
Leetcode 42 Trapped water
思路: 由于蓄水是两高夹一底的模块,求每一个位置的蓄水量,我们需要向左向右找到最近的最大值;向右的最大值可以在向后循环中直接判断,向左的最大值通过从左向右维护一个递减栈来找到。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public : int trap(vector<int >& height ) { int ans = 0 , current = 0 ; stack<int > st; while (current < height .size ()) { while (!st.empty() && height [current]>=height [st.top()]) { int top = st.top(); st.pop(); if (st.empty()) break ; int distance = current - st.top() - 1 ; int bounded_height = min (height [current], height [st.top()]) - height [top]; ans += distance * bounded_height; } st.push(current++); } return ans; } };
Leetcode 901. Online Stock Span
For example, if the price of a stock over the next 7 days were [100, 80, 60, 70, 60, 75, 85], then the stock spans would be [1, 1, 1, 2, 1, 4, 6].
思路:这一题要求找到左边比其小的最长序列的长度。很容易被误认为是找左边第一个最小而使用递增栈。但是仔细分析,左边比其小的最长序列,其实是求左边第一个比其大的元素的位置,这之间就是所要求的序列。所以应该使用递减栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class StockSpanner {public : StockSpanner() { } int next (int price) { int span = 1 ; while (!s_.empty() && price >= s_.top().first) { span += s_.top().second; s_.pop(); } s_.emplace(price, span); return span; } private : stack <pair<int ,int >> s_; };
Case 2
Leetcode 84. Largest Rectangle in Histogram
思路:将问题转化为求以某一高度nums[i]为高度的矩形的最大面积,即给定高度,我们向左向右寻找边界(第一个比nums[i]小的位置 )。如图:对于nums[2]=5, 左边界index为1, 右边界为4, 所以可以构成的矩形面积为nums[2]*(4-2-1). 所以这边使用单调递增栈。
这边在原序列左后增加一个0元素,可以方便处理边界(0小于序列中的所有元素,所以肯定会触发操作)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution {public : int largestRectangleArea (vector <int >& heights) { stack <int > st; heights.push_back(0 ); int res = 0 ; for (int i=0 ; i<heights.size(); ++i) { while (!st.empty() && heights[i]<heights[st.top()]) { int curHeight = heights[st.top()]; st.pop(); int left = st.empty() ? -1 : st.top(); res = max(res, curHeight*(i-left-1 )); } st.push(i); } return res; } };
Case 3
Leetcode 496. Next Greater Element I
Input: nums1 = [4,1,2], nums2 = [1,3,4,2].
Output: [-1,3,-1]
思路:求右边第一个比nums[i]大的位置,从右向左构建一个单调递减栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution {public : vector <int > nextGreaterElement(vector <int >& nums1, vector <int >& nums2) { vector <int > res; stack <int > st; unordered_map <int ,int > indices; for (int i=nums2.size()-1 ; i>=0 ; --i) { while (!st.empty() && nums2[i]>st.top()) st.pop(); if (st.empty()) indices[nums2[i]] = -1 ; else indices[nums2[i]] = st.top(); st.push(nums2[i]); } for (int i=0 ; i<nums1.size(); ++i) res.push_back(indices[nums1[i]]); return res; } };
类似问题:
503. Next Greater Element II
739. Daily Temperatures
Case 4
做题太少,还没遇到这种类型的题目。