package com.heu.wsq.leetcode.dp;
import java.util.Deque;
import java.util.LinkedList;
/**
* 面试题 17.21. 直方图的水量
* @author wsq
* @date 2021/4/2
* 给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
*
* 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。
*
* 示例:
* 输入: [0,1,0,2,1,0,1,3,2,1,2,1]
* 输出: 6
*
* 链接:https://leetcode-cn.com/problems/volume-of-histogram-lcci
*/
public class Trap {
/**
* 对于某一个位置上能存多少水,是由其两侧的最高柱形的高度最小值决定,最小值需减去当前柱形的高度
* 即是 water[i] = min(leftMax, rightMax) - height[i]
* 减去height[i]必须减去当前柱形的高度
*/
public int trap(int[] height) {
int n = height.length;
if(n == 0){
return 0;
}
// 使用leftMaxs保存每个每个位置对应左边的最高柱形高度
int[] leftMaxs = new int[n];
leftMaxs[0] = height[0];
for(int i = 1; i < n; i++){
leftMaxs[i] = Math.max(leftMaxs[i-1], height[i]);
}
// 使用rightMaxs保存每个位置右边的最高柱形高度
int[] rightMaxs = new int[n];
rightMaxs[n-1] = height[n-1];
for(int i = n-2; i >= 0; i--){
rightMaxs[i] = Math.max(rightMaxs[i+1], height[i]);
}
// 根据左右高度,求解对应每个位置上能存放水的高度
int ans = 0;
for(int i = 0; i < n; i++){
ans += Math.min(leftMaxs[i], rightMaxs[i]) - height[i];
}
return ans;
}
public int trap2(int[] height){
int n = height.length;
if(n == 0){
return 0;
}
// 构建单调栈,保持栈中的元素顺序是递减的,遇见比栈顶元素大的,则将其弹出,
// 根据两端的高度最小值,计算弹出栈顶元素存水的多少
Deque<Integer> stack = new LinkedList<>();
int ans = 0;
for(int i = 0; i < n; i++){
while(!stack.isEmpty() && height[i] > height[stack.peekLast()]){
Integer pop = stack.pop();
if(stack.isEmpty()){
break;
}
int currW = i - stack.peekLast() - 1;
int currH = Math.min(height[i], height[stack.peekLast()]) - height[pop];
ans += currH * currW;
}
stack.offerLast(i);
}
return ans;
}
}