什么是:
Monotone Stack
-
单调栈中存放的数据应该是有序的,所以单调栈也分为单调递增栈和单调递减栈
单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大
调栈的一大优势就是线性的时间复杂度,所有的元素只会进栈一次,而且一旦出栈后就不会再进来了。
适用类型
其实单调栈就是从数组中找到左右两边比你大的数或者比你小的数而且时间复杂度为O(N)
也就是说在队列或数组中,我们需要通过比较前后元素的大小关系来解决问题时我们通常使用单调栈。比如找后边第一个大于它的元素。
答题模板积累
题目汇总
以下题目都是,一个元素找到后边第一个大于它的元素,然后进行的操作。构造单调栈:从栈底到栈顶由大到小。
Leetcode 496. 下一个更大元素 I
题目描述
给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。
请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
class Solution {
//思路 :单调栈,从栈底到栈顶单调递减的栈
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
unordered_map<int,int> map; //中间哈希表,存放num2中所有元素的右侧第一个大于它的元素
for(int i=0;i<nums2.size();i++){
map[nums2[i]]=-1; //初始化哈希表
}
vector<int> res(nums1.size(),-1); //返回值
stack<int> sta; //单调栈
//更新哈希表
for(int k=0;k<nums2.size();k++){
while(!sta.empty() &&nums2[sta.top()]<nums2[k]){
int cur=sta.top();
sta.pop();
map[nums2[cur]]=nums2[k];
}
sta.push(k);
}
//nums1需要多少就从哈希表中取多少
for(int i=0;i<nums1.size();i++){
res[i]=map[nums1[i]];
}
return res;
}
};
Leetcode 503. 下一个更大元素 II
题目描述
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
注意: 输入数组的长度不会超过 10000。
class Solution {
public:
//思路:一次单调栈适合求一个方向上求第一个大(小)的值,这是个循环数组,应对循环数组的技巧:使用取余操作
//思路是历到的元素大于栈顶元素了,就找到了第一个大于栈顶元素的值,栈顶元素出栈,遍历到的元素进栈
vector<int> nextGreaterElements(vector<int>& nums) {
vector<int> res(nums.size(),-1); //返回
stack<int> sta; //单调栈
int n=nums.size();
for(int i=0;i<n*2;i++){
while( !sta.empty() && nums[sta.top()]<nums[i%n]){
int cur=sta.top();
sta.pop();
res[cur]=nums[i%n];
}
sta.push(i%n);
}
return res;
}
};
Leetcode 42. 接雨水
题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXyvB1DX-1615687278117)(20210307152923533.png)]
class Solution {
/*
思路:类似于下边的求柱子构成的矩形的最大面积
构造一个从底部到顶部单调递减的栈,当遍历的一个柱子比栈顶柱子高的时候就会构成一个凹槽,每次加上min(当前遍历柱子,栈顶柱子的前一个柱子)*两者索引距离。
遍历到的柱子大于栈顶的柱子,就构成凹槽了,注意出栈的时候将相同的栈顶元素也一起出栈
单调递减栈:从底到高单调递减
*/
public:
int trap(vector<int>& height) {
//注意特殊情况
if(height.size()==0){
return 0;
}
int sum=0;
stack<int> sta;
for(int i=0;i<height.size();i++){
while(!sta.empty() && height[i]>height[sta.top()]){
int cur=sta.top();
//将栈顶元素出栈,并且把所有和栈顶元素相同的也一起出栈
sta.pop();
while(!sta.empty() && height[sta.top()]==height[cur]){
sta.pop();
}
if(!sta.empty())//注意这里
sum+=(min(height[i],height[sta.top()])-height[cur])*(i-sta.top()-1);
}
sta.push(i);
}
return sum;
}
};
Leetcode 739. 每日温度
题目描述
请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。
class Solution {
/*
单调栈:找元素A后边出现的第一个大于它的元素B,求A,B间距离
单调递增栈:从栈底到栈顶从大到小
*/
public:
vector<int> dailyTemperatures(vector<int>& T) {
stack<int> sta;
vector<int> res(T.size(),0); //返回值
for(int i=0;i<T.size();i++){
while(!sta.empty() && T[sta.top()]<T[i]){
int cur=sta.top();
sta.pop();
res[cur]=i-cur;
}
sta.push(i);
}
return res;
}
};
下边这个和上边的有些不同,这个单调栈是由底至顶由小到大。
Leetcode 84. 柱状图中最大的矩形
.题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bN5s1WOE-1615687278119)(20210307105005177.png)]
class Solution {
/*思路:单调栈,栈内由底到顶单调增
从左到右遍历每一个柱子,若柱子遇到高的,则可以继续扩展,只到遇到一个低于它的柱子,则以这个柱子的高为高,以这个柱子的右边第一个低于它的柱子的索引和这个柱子前边的一个柱子的索引可以确定矩形的宽。因此可求出当前柱子为高的矩形面积,当前柱子出栈,右边第一个低于它的柱子入栈。
整体思路:从左到右入,从右向左出
栈中存储的是元素的下标,因为要用于求矩形的宽
*/
public:
int largestRectangleArea(vector<int>& heights) {
if(heights.size()==0) return 0;
stack<int> sta;
int this_max=0; //最大面积
//避免栈低和栈顶元素无法出栈,因此列表两端追加元素0
heights.insert(heights.begin(),0);
heights.push_back(0);
//初始时栈为空,入栈
for(int i=0;i<heights.size();i++){
// 如果栈不为空且当前考察的元素值小于栈顶元素值,
while(!sta.empty()&&heights[sta.top()]>heights[i]){
int cur=sta.top();
sta.pop();
int top=sta.top();
this_max=max(this_max,heights[cur]*(i-top-1));
}
sta.push(i);
}
return this_max;
}
};