单调栈核心思想
1、使用单调栈的场景
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。
2、单调栈的本质是空间交换时间,栈中记录着之前的元素。
3、需要考虑单调栈中存放什么元素
4、单调栈的三个判断条件
- 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
- 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
下一个元素更大I
class Solution {
// public int[] nextGreaterElement(int[] nums1, int[] nums2) {
// int[] result = new int[nums1.length];
// Arrays.fill(result, -1);
// int idx = 0;
// Map<Integer, Integer> map = new HashMap<>();
// for (int num : nums2) {
// map.put(num, idx++);
// }
// for (int i = 0; i < nums1.length; ++i) {
// for (int j = map.get(nums1[i]) + 1; j < nums2.length; ++j) {
// if (nums2[j] > nums1[i]) {
// result[i] = nums2[j];
// break;
// }
// }
// }
// return result;
// }
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] result = new int[nums1.length];
Arrays.fill(result, -1);
Map<Integer, Integer> map = new HashMap<>();
int idx = 0;
for (int num : nums1) {
map.put(num, idx++);
}
Stack<Integer> stack = new Stack<>();
stack.push(0);
for (int i = 1; i < nums2.length; ++i) {
if (nums2[i] <= nums2[stack.peek()]) {
stack.push(i);
}
else {
while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
if (map.containsKey(nums2[stack.peek()])) {
result[map.get(nums2[stack.peek()])] = nums2[i];
}
stack.pop();
}
stack.push(i);
}
}
return result;
}
}
写了两种方法,第一种是暴力方法,第二种是单调栈的方法,栈的顺序是递增顺序。即栈头最小
下一个更大元素II
class Solution {
public int[] nextGreaterElements(int[] nums) {
int[] result = new int[nums.length];
Arrays.fill(result, -1);
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < nums.length * 2; ++i) {
while (!stack.isEmpty() && nums[i % nums.length] > nums[stack.peek()]) {
result[stack.peek()] = nums[i % nums.length];
stack.pop();
}
stack.push(i % nums.length);
}
return result;
}
}
很巧妙的一个用法,就是循环遍历的时候我们可以遍历二倍的长度,数据索引通过当前遍历的索引除以数组长度取余
每日温度
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
Stack<Integer> stack = new Stack<>();
stack.push(0);
for (int i = 1; i < n; ++i) {
if (temperatures[i] <= temperatures[stack.peek()]) {
stack.push(i);
}
else {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
answer[stack.peek()] = i - stack.peek();
stack.pop();
}
stack.push(i);
}
}
return answer;
}
}
接雨水
class Solution {
public int trap(int[] height) {
int size = height.length;
if (size <= 2) {
return 0;
}
Stack<Integer> stack = new Stack<>();
stack.push(0);
int sum = 0;
for (int i = 1; i < size; ++i) {
if (height[i] < height[stack.peek()]) {
stack.push(i);
}
else if (height[i] == height[stack.peek()]) {
stack.pop();
stack.push(i);
}
else {
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int mid = stack.pop();
if (!stack.isEmpty()) {
int left = stack.peek();
int height_slot = Math.min(height[left], height[i]) - height[mid];
int width = i - left - 1;
int area = height_slot * width;
if (area > 0) {
sum += area;
}
}
}
stack.push(i);
}
}
return sum;
}
}
单调栈递增顺序(栈头元素最小),分别考虑三个判断条件
柱状图中最大的矩形
class Solution {
public int largestRectangleArea(int[] heights) {
int[] heights_new = new int[heights.length + 2];
heights_new[0] = 0;
heights_new[heights_new.length - 1] = 0;
for (int index = 0; index < heights.length; ++index) {
heights_new[index + 1] = heights[index];
}
Stack<Integer> stack = new Stack<>();
stack.push(0);
int result = 0;
for (int i = 1; i < heights_new.length; ++i) {
if (heights_new[i] > heights_new[stack.peek()]) {
stack.push(i);
}
else if (heights_new[i] == heights_new[stack.peek()]) {
stack.pop();
stack.push(i);
}
else {
while (!stack.isEmpty() && heights_new[i] < heights_new[stack.peek()]) {
int mid = stack.pop();
if (!stack.isEmpty()) {
int left = stack.peek();
int width = i - left - 1;
int height_slot = heights_new[mid];
result = Math.max(result, width * height_slot);
}
}
stack.push(i);
}
}
return result;
}
}
单调栈,递减的顺序(栈头最大),分别考虑三个判断条件。这题与接雨水是正好相反的两道题,可以对比参考一下