单调栈
理解:栈内元素的大小按照它们在栈内的位置,满足一定的单调性。这种数据结构通常运用在一维数组上。如果遇到问题与前后元素之间的大小关系有联系的话(如leetcode 84/85/42),我们可以试图用单调栈来解决。使用单调栈,关键在于每个元素出栈的意义。由于每个元素都出栈入栈一次,时间复杂度O(n)。
例1:
#include<bits/stdc++.h> using namespace std; int main() { int n; cin>>n; vector<int>v(n); for(int i=0;i<n;++i) cin>>v[i]; // 维护一个单调递增栈 stack<int>s; // 对每个位置 要求输出的索引对 map<int,pair<int,int>>mp; for(int i=0;i<n;++i) { // 当前元素小于栈顶元素,那么栈顶元素要求的左右两个位置已经得到 while(!s.empty()&&v[i]<v[s.top()]) { int cur = s.top(); s.pop(); int left = s.empty() ? -1:s.top(); mp[cur] = make_pair(left,i); } s.push(i); } // 结算阶段 (所有元素右侧均没有比它自己小的元素) while(!s.empty()) { int cur = s.top(); s.pop(); int left = s.empty() ? -1:s.top(); mp[cur] = make_pair(left,-1); } for(auto it = mp.begin();it!=mp.end();it++) { cout<<it->second.first<<" "<<it->second.second<<endl; } return 0; }
例2.接雨水
思路:每个位置的盛水量取决于min(该位置左边高度最大值,该位置右边高度最大值)-这个位置本身的高度。
法一:求解出每个位置左右两边最大高度值并记录下来。接下来累加每个位置的接水量即可。O(n)。
法二:单调栈法。维护一个单调递减栈。如果当前的条形块小于或等于栈顶的条形块,我们将条形块的索引入栈,意思是当前的条形块被栈中的前一个条形块界定;如果我们发现一个条形块长于栈顶,我们可以确定栈顶的条形块被当前条形块和栈的前一个条形块界定,因此我们可以弹出栈顶元素并且累加到结果中。O(n)。
class Solution { public: int trap(vector<int>& height) { // 单调递减栈法 stack<int>s; int ans = 0; for(int i=0;i<height.size();++i) { // 形成一个能盛水的容器 while(!s.empty()&&height[i]>height[s.top()]) { int cur = s.top(); s.pop(); if(s.empty()) break; // 容器两侧相隔的距离 int distance = i - s.top()-1; { // 盛水量取决于较短的边,且要减去当前位置占据的空间 ans += (min(height[i],height[s.top()]) - height[cur])*distance; } } s.push(i); } return ans; } };