单调栈模板
主要参考:https://www.bilibili.com/video/BV1my4y1Z7jj/
四种情况代码模板,分别对应查找数组中每个数左边/右边第一个比它大/小的元素下标。
package test;
import java.util.ArrayDeque;
import java.util.Deque;
class MonoStack{
public static void MonoStackFunc(int[] arrs) {
int n = arrs.length;
// 1.寻找左侧第一个比当前元素大的元素 下标
int[] leftMax = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
int x = arrs[i];
// 构造单调递增栈
while(!st.isEmpty() && x >= arrs[st.peek()]){
st.pop();
}
// stack栈顶 存的都是最右、最大的值 下标
leftMax[i] = st.isEmpty() ? -1:st.peek();
st.push(i);
}
// 2.寻找左侧第一个比当前元素小的元素 下标
int[] leftMin = new int[n];
st.clear();
for (int i = 0; i < n; i++) {
int x = arrs[i];
while (!st.isEmpty() && x <= arrs[st.peek()]) {
st.pop();
}
leftMin[i] = st.isEmpty() ? -1 : st.peek();
st.push(i);
}
// 3.1.寻找右侧第一个比当前元素大的元素 下标
int[] rightMax = new int[n];
st.clear();
for (int i = 0; i < n; i++) {
int x= arrs[i];
// stack从栈顶开始,如果i为比较大的数的下标,则找到了自己要的结果
while(!st.isEmpty() && x > arrs[st.peek()]){
rightMax[st.peek()] = i;
st.pop();
}
// 找不到的先存起来
st.push(i);
}
// 没找到的默认设置为长度n
while(!st.isEmpty()){rightMax[st.pop()] = n;}
// 3.2.寻找右侧第一个比当前元素大的元素 下标【写法2】
int[] rightMax2 = new int[n];
st.clear();
for (int i = n-1; i >= 0; i--) {
int x= arrs[i];
while(!st.isEmpty() && x >= arrs[st.peek()]){
st.pop();
}
rightMax2[i] = st.isEmpty()? n:st.peek();
st.push(i);
}
// 4.1.寻找右侧第一个比当前元素小的元素 下标
int[] rightMin = new int[n];
st.clear();
for (int i = 0; i < n; i++) {
int x = arrs[i];
while(!st.isEmpty() && x < arrs[st.peek()]){
rightMin[st.peek()] = i;
st.pop();
}
st.push(i);
}
while(!st.isEmpty()){rightMin[st.pop()] = n;}
// 4.2.寻找右侧第一个比当前元素小的元素 下标【写法2】
int[] rightMin2 = new int[n];
st.clear();
for (int i = n-1; i >= 0; i--) {
int x= arrs[i];
while(!st.isEmpty() && x <= arrs[st.peek()]){
st.pop();
}
rightMin2[i] = st.isEmpty()? n: st.peek();
st.push(i);
}
return;
}
public static void main(String[] args) {
MonoStackFunc(new int[]{2,1,5,6,2,3});
return;
}
}
力扣 739. 每日温度
该问题相当于要先获得数组中每个数右边第一个比它大的元素下标,因此可以用3.1或3.2的方式解决。下面给出两种解法。
// 3.1
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] res = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
int x = temperatures[i];
while(!st.isEmpty() && x > temperatures[st.peek()]){
res[st.peek()] = i - st.peek();
st.pop();
}
st.push(i);
}
return res;
}
// 3.2
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] res = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = n-1; i >= 0; i--) {
int x = temperatures[i];
while(!st.isEmpty() && x >= temperatures[st.peek()]){
st.pop();
}
res[i] = st.isEmpty() ? 0 : st.peek() - i;
st.push(i);
}
return res;
}
力扣 84. 柱状图中最大的矩形
主要参考灵茶山艾府的代码。
其中寻找右边第一个比他小的下标实现了2种方式。
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
int x = heights[i];
while(!st.isEmpty() && x <= heights[st.peek()]){
st.pop();
}
left[i] = st.isEmpty()? -1:st.peek();
st.push(i);
}
int[] right = new int[n];
st.clear();
// 4.2 寻找右侧第一个比当前元素小的元素 下标
/*
for (int i = 0; i < n; i++) {
int x = heights[i];
while(!st.isEmpty() && x < heights[st.peek()]){
right[st.peek()] = i;
st.pop();
}
st.push(i);
}
while(!st.isEmpty()){right[st.pop()] = n;}
*/
// 4.1
for (int i = n-1; i >= 0; i--) {
int x = heights[i];
while(!st.isEmpty() && x <= heights[st.peek()]){
st.pop();
}
right[i] = st.isEmpty()? n:st.peek();
st.push(i);
}
int res = 0;
for (int i = 0; i < n; i++) {
int width = right[i] - left[i] -1;
res = Math.max(res, heights[i] * width);
}
return res;
}
参考文献
理论介绍:
https://www.bilibili.com/video/BV1VN411J7S7/
各种情况分析:
https://blog.csdn.net/zy_dreamer/article/details/131036101