单调栈 LeetCode
单调栈
单调栈:这题考的基础模型其实就是:在一维数组中对每一个数找到第一个比自己小的元素。
这类“在一维数组中找第一个满足某种条件的数”的场景就是典型的单调栈应用场景。
重要的是:将问题转化成找到下一个 比当前值大/小的 值的下标
package leetcode;
import java.util.*;
public class MonotonousStackDemo {
public static void main(String[] args) {
}
}
/**
* 84. 柱状图中最大的矩形
*/
/*
空间优化
概括的说:矩形高度相等的时候,虽然按照左边柱子来算,答案不对,但是最右边的来算,结果是对的,我们只需要保存一个正确的就可。
等等,我们需要的是「一根柱子的右侧且最近的小于其高度的柱子」,但这里我们求的是小于等于,那么会造成什么影响呢?
答案是:我们确实无法求出正确的右边界,但对最终的答案没有任何影响。
这是因为在答案对应的矩形中,如果有若干个柱子的高度都等于矩形的高度,那么最右侧的那根柱子是可以求出正确的右边界的,
而我们没有对求出左边界的算法进行任何改动,因此最终的答案还是可以从最右侧的与矩形高度相同的柱子求得的。
*/
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Arrays.fill(right, n);
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
right[stack.pop()] = i;
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
int area = (right[i] - left[i] - 1) * heights[i];
ans = Math.max(ans, area);
}
return ans;
}
}
// 先左后右,记住当前柱子左侧和右侧小于其高度的柱子下标
class Solution130 {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
// 忘记清除
stack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
stack.pop();
}
right[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
int area = (right[i] - left[i] - 1) * heights[i];
ans = Math.max(ans, area);
}
return ans;
}
}
/**
* 42. 接雨水
*/
// 单调栈,按行计算;前两种方法用的都是按列计算雨水数量
class Solution129 {
public int trap(int[] height) {
int ans = 0;
Deque<Integer> stack = new LinkedList<>();
int n = height.length;
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int top = stack.pop();
if (stack.isEmpty()) break;
int left = stack.peek();
int wid = i - left - 1;
int hgh = Math.min(height[left], height[i]) - height[top];
ans += wid * hgh;
}
stack.push(i);
}
return ans;
}
}
// 动态规划
class Solution128 {
public int trap(int[] height) {
int n = height.length;
int[] lMax = new int[n];
int[] rMax = new int[n];
lMax[0] = height[0];
for (int i = 1; i < n; i++) {
lMax[i] = Math.max(lMax[i - 1], height[i]);
}
rMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; i--) {
rMax[i] = Math.max(rMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; i++) {
int count = Math.min(lMax[i], rMax[i]) - height[i];
System.out.println(ans);
if (count > 0) ans += count;
}
System.out.println(Arrays.toString(lMax));
System.out.println(Arrays.toString(rMax));
return ans;
}
}
// 巧妙的双指针
class Solution127 {
public int trap(int[] height) {
int ans = 0;
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
// left < right;相遇的时候,left与right一定为最大值
// left <= right也可以,最大值时计算ans += 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
left++;
} else {
ans += rightMax - height[right];
right--;
}
}
return ans;
}
}
/**
* 503. 下一个更大元素 II
*/
class Solution126 {
public int[] nextGreaterElements(int[] t) {
LinkedList<Integer> stack = new LinkedList<>();
int[] ret = new int[t.length];
Arrays.fill(ret, -1);
for (int i = 0; i < t.length * 2; i++) {
int j = i % t.length;
while (!stack.isEmpty() && t[j] > t[stack.peek()]) {
ret[stack.peek()] = t[j];
stack.pop();
}
stack.push(j);
}
return ret;
}
}
/**
* 496. 下一个更大元素 I
*/
class Solution125 {
public int[] nextGreaterElement(int[] nums1, int[] nums2) {
int[] ret = new int[nums1.length];
Arrays.fill(ret, -1);
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums1.length; i++) {
map.put(nums1[i], i);
}
LinkedList<Integer> stack = new LinkedList<>();
stack.push(0);
for (int i = 0; i < nums2.length; i++) {
while (!stack.isEmpty() && nums2[i] > nums2[stack.peek()]) {
int val = nums2[stack.peek()];
if (map.containsKey(val)) {
ret[map.get(val)] = nums2[i];
}
stack.pop();
}
stack.push(i);
}
return ret;
}
}
/**
* 739. 每日温度
*/
// 使用LinkedList当做栈比Stack要快很多啊
// Stack底层实现是数组
class Solution124 {
public int[] dailyTemperatures(int[] t) {
LinkedList<Integer> stack = new LinkedList<>();
int[] ret = new int[t.length];
stack.push(0);
for (int i = 1; i < t.length; i++) {
while (!stack.isEmpty() && t[i] > t[stack.peek()]) {
ret[stack.peek()] = i - stack.pop();
}
stack.push(i);
}
return ret;
}
}
class Solution123 {
public int[] dailyTemperatures(int[] t) {
Stack<Integer> stack = new Stack<>();
int[] ret = new int[t.length];
stack.push(0);
for (int i = 1; i < t.length; i++) {
while (!stack.isEmpty() && t[i] > t[stack.peek()]) {
ret[stack.peek()] = i - stack.pop();
}
stack.push(i);
}
return ret;
}
}