单调栈解题模板
单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减)。
本文就通过几道算法题来看看单调栈模板的使用。
一.单调栈模板
给一个数组,返回一个等长的数组,对应索引存储着下一个更大元素,如果没有更大的元素,就存 -1。
函数签名如下:
vector<int> nextGreaterElement(vector<int>& nums);
比如说,输入一个数组nums = [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。
暴力解很容易想到:
public int[] nextGreaterElement(int[]nums){
int n = nums.length;
int[] nums1 = new int[n];
int index=0;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(nums[j]>nums[i])
nums1[index++] =nums[j];
break;
nums1[index++]=-1;
}
}
}
这个问题可以这样抽象思考:把数组的元素想象成并列站立的人,元素大小想象成人的身高。这些人面对你站成一列,如何求元素「2」的 Next Greater Number 呢?很简单,如果能够看到元素「2」,那么他后面可见的第一个人就是「2」的 Next Greater Number,因为比「2」小的元素身高不够,都被「2」挡住了,第一个露出来的就是答案。
public int[] nextGreaterElement(int[]nums){
int nums3 = new int[nums.length];
Stack<Integer>st =new Stack<Integer>();
for(int i=nums.length-1;i>=0;i--){
//判断个子高矮
while(!st.isEmpty()&&st.peek()<=nums[i])
//矮的就去掉
st.pop();
//这时候还能看见的第一个,说明就是最近的比本数大的。
nums3[i] =st.isEmpty()?-1:st.peek();
st.push(nums[i]);
}
return nums3;
}
二.问题变形
这次相当于是数组里存放的不是数本身,而是对应的索引间距。
public int[] dailyTemp(int []T){
int n = T.length;
int []nums = new int[n];
//单调栈用来存放高的元素
Stack<Integer> st = new Stack<>();
//从后往前放入
for(int i =n-1;i>=0;i--){
while(!st.isEmpty()&&T[st.peek()]<=T[i])
//温度低的都抛掉
st.pop();
nums[i] = st.isEmpty()?0:(st.peek()-i);
st.push(i);
}
return nums;
}
三. 如何处理环形数组
这里主要得熟悉如何构造环形数组,一般通过取模操作:
int[] arr = {1,2,3,4,5};
int n = arr.length, index = 0;
while (true) {
print(arr[index % n]);
index++;
}
常用套路是将数组长度翻倍,但这里可以不用额外构造,使用循环数组实现:
public int[] nextGreaterElements(int[] nums) {
int n= nums.length;
int [] nums1 = new int [n];
Stack<Integer>st = new Stack<>();
for(int i=2*n-1;i>=0;i--){
while(!st.isEmpty()&& st.peek()<=nums[i%n])
st.pop();
nums1[i%n] = st.isEmpty()?-1:st.peek();
st.push(nums[i%n]);
}
return nums1;
}