对于一个这样单调递增栈(从栈顶到栈底越来越大,金字塔模型)
当进来的元素值小于栈顶元素值,那就直接进栈,因为进栈还是可以维持这个栈是一个单调递增栈
当进来的元素值大于现在栈里面最大的栈(如下图所示),此时就要先把栈里面的所有元素都移出栈,然后这个元素进栈
问题模型:给定一个序列,求这个序列当中,每一个数左边比它小而且离它最近的数(或者右边,比它大而且离它最近的数)
比如:给定一个数组,求每个元素左侧比它小,而且离它最近的元素是多少
维护一个单调递增的栈(越往上越大)
模板:(每日温度这道题的代码就是模板)
class Solution
{
public int[] dailyTemperatures(int[] nums)
{
int[] result=new int[nums.length];
Stack<Integer> stack=new Stack();
for(int i=0;i<nums.length;i++)//将温度数组中每一个元素的索引尝试进栈
{
if(stack.size()==0)
{
stack.push(i);
continue;
}
if(stack.size()!=0&&nums[i]<=nums[stack.peek()])//比栈顶元素小,直接进栈
{
stack.push(i);
continue;
}
if(stack.size()!=0&&nums[i]>nums[stack.peek()])//栈内不为空
{
while(stack.size()!=0&&nums[i]>nums[stack.peek()])
{
result[stack.peek()]=i-stack.peek();
stack.pop();
}
stack.push(i);
continue;
}
}
return result;
}
}
其实这个代码可以简化:那是我是不会记这个模板的,我还是喜欢代码写的稍微罗嗦一些,但是能一样就看懂
class Solution
{
public int[] dailyTemperatures(int[] nums)
{
int[] result=new int[nums.length];
Stack<Integer> stack=new Stack();
for(int i=0;i<nums.length;i++)//将温度数组中每一个元素的索引尝试进栈
{
while(stack.size()!=0&&nums[i]>nums[stack.peek()])
{
result[stack.peek()]=i-stack.peek();
stack.pop();
}
stack.push(i);
}
return result;
}
}
单调栈还是栈,只是通过一些设置,使得每次新元素进栈后,栈内的元素都保持有序(单调递增或者单调递减)
比如构建一个单调递增栈:
从栈顶到栈底,元素大小依次递增(也就是说越往下,元素越大)
比如:
一维数组中,寻找任意一个元素右边第一个比自己大的数,或者是一个元素左边第一个比自己小的数的时候就要用到单调栈
例题1:力扣739 每日温度
题目意思:找出每一天要过几天才能碰到一个更高的温度,也就是说要找出数组中第一个比当前元素大的元素,然后算出这两个元素之间的下标之差
nums = [73, 74, 75, 71, 71, 72, 76, 73]
将温度数组中每一个元素的索引尝试进栈
如果比栈顶元素大,那说明栈顶元素的下一个更大元素就是要进栈的元素
如果比栈顶元素小,那直接进栈
class Solution
{
public int[] dailyTemperatures(int[] nums)
{
int[] result=new int[nums.length];
Stack<Integer> stack=new Stack();
for(int i=0;i<nums.length;i++)//将温度数组中每一个元素的索引尝试进栈
{
if(stack.size()==0)
{
stack.push(i);
continue;
}
if(stack.size()!=0&&nums[i]>nums[stack.peek()])//栈内不为空
{
while(stack.size()!=0&&nums[i]>nums[stack.peek()])
{
result[stack.peek()]=i-stack.peek();
stack.pop();
}
stack.push(i);
continue;
}
if(stack.size()!=0&&nums[i]<=nums[stack.peek()])//比栈顶元素小,直接进栈
{
stack.push(i);
continue;
}
}
return result;
}
}
例题2
先用一个HahMap存起来nums2数组中每一个元素的下一个更大元素,然后将nums1数组中的元素作为key从这个HahMap中取出value即可
输入一个数组 nums
,请你返回一个等长的数组,这个数组中的元素是nums数组中每个元素下一个更大的元素(如果后面没有比这个数更大的数,那就存-1)
class Solution {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
HashMap<Integer,Integer> map=new HashMap();
Stack<Integer> stack=new Stack();
for(int i=0;i<nums2.length;i++)
{
if(stack.size()==0)
{
stack.push(i);
continue;
}
if(stack.size()!=0&&nums2[i]<=nums2[stack.peek()])
{
stack.push(i);
continue;
}
if(stack.size()!=0&&nums2[i]>nums2[stack.peek()])//尝试进栈的元素比栈顶元素大
{
while(stack.size()!=0&&nums2[i]>nums2[stack.peek()])
{
map.put(nums2[stack.peek()],nums2[i]);
stack.pop();
}
stack.push(i);
continue;
}
}
int[] result=new int[nums1.length];
for(int i=0;i<nums1.length;i++)
{
result[i]=map.getOrDefault(nums1[i],-1);//nums2中有些元素可能没有下一个更大的元素·
}
return result;
}
}
例题3
力扣 84 柱状图中最大的矩形
找出下一个更小的数,不是金字塔模型,而是倒金字塔模型
找到每个元素左边第一个更小的元素索引left,右边第一个更小的元素索引right,要注意的是这两个是边界,不能包括这两个边界,所以宽度应该是(right-1)-(left+1)+1=right-left-1那么以这个元素作为高可以形成的最大矩形就是nums[i]*(ritgh-left-1)
比如这里5这个元素,向左找到的第一个更小元素索引是1,向右找到的第一个更小元素索引是4,
,所以其实最大的矩形就是5*2=10,如右图
接下来用单调栈构造两个数组:left数组装每个元素左边第一个更小元素的下标,right数组装每个元素右边第一个更小元素的下标
这里的问题在于如果在左边找不到第一个更小的元素或者在右边找不到第一个更小的元素怎么办,如果左边找不到第一个比当前元素小的元素,就将其left数组索引置为-1,
右边找不到第一个比当前元素小的元素,就将其right数组该位置索引置为nums.length
class Solution {
public int largestRectangleArea(int[] heights) {
int n=heights.length;
Stack<Integer> stack1=new Stack();
Stack<Integer> stack2=new Stack();
//找出每个元素右边第一个更小的元素
int[] right=new int[n];
Arrays.fill(right,n);
for(int i=0;i<n;i++)
{
while(stack1.size()!=0&&heights[i]<heights[stack1.peek()])
{
right[stack1.peek()]=i;
stack1.pop();
}
stack1.push(i);
}
//找出每个元素左边第一个更小的元素
int[] left=new int[n];
Arrays.fill(left,-1);
for(int i=n-1;i>=0;i--)
{
while(stack2.size()!=0&&heights[i]<heights[stack2.peek()])
{
left[stack2.peek()]=i;
stack2.pop();
}
stack2.push(i);
}
int result=0;
for(int i=0;i<n;i++)
{
result=Math.max(result,heights[i]*(right[i]-left[i]-1));
}
return result;
}
}