leetCode 第42题,单调栈典型
//给定 n 个非负整数表示每个宽度为 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
// Related Topics 栈 数组 双指针
import java.util.Stack;
//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int trap(int[] height) {
if (height == null || height.length==0)
return 0;
//维护降序栈:遍历数组时入栈,入栈规则,当前元素小于栈顶入栈,大于栈顶时,需要依次出站,一直至当前元素小于栈顶
//当前元素大于栈顶元素时,当前元素为右边界right。栈顶元素的前一个元素为左边界left(降序栈)。
//雨水面积为(min(left,rihht)-hight[stack.peek](弹出元素高度)) * (right -left -1),当前元素大于若干个栈元素时,便要计算
//面积的宽度(right -left -1)
int area = 0;
Stack<Integer> stack = new Stack<Integer>();
for(int right = 0 ;right<height.length;right++){
//大于栈顶时,需要依次出站,一直至当前元素小于栈顶,故不能用if
while(!stack.empty() && height[right]>height[stack.peek()]){
//弹出元素的高度
int top = stack.pop();
if(stack.empty()){
break;
}
int left = stack.peek();
area += (Math.min(height[left],height[right]) - height[top]) * (right-left-1);
}
stack.push(right);
}
return area;
//数组,当前元素i接水的高度,枚举左右边界max,左边界left为数组[0,i-1]高度Max,右边界right为数组[i+1,nums.length-1]高度Max
//由于是逐个元素遍历,故宽度是1
//改进,增加leftMax数组和rightMax数组缓存当前元素的leftMax及rightMax
int area = 0;
for(int i=0;i<height.length;i++){
//当i在为0和height.length-1时,为数组的左右边界,此时不会储水
if(i==0 || i== height.length-1){
continue;
}
//在[0,i-1]中找leftMax
int leftMax = 0;
for(int j=i;j>=0;j--){
leftMax = Math.max(leftMax,height[j]);
}
//在[i+1,nums.length-1]中找rightMax
int rightMax =0;
for(int k=i;k<height.length;k++){
rightMax = Math.max(rightMax,height[k]);
}
area += Math.min(leftMax,rightMax)-height[i];
}
return area;
//改进,增加leftMax数组和rightMax数组缓存当前元素的leftMax及rightMax
int area =0;
int[] leftMax = new int[height.length];
int[] rightMax = new int[height.length];
leftMax[0] =height[0];
for (int i =1; i<height.length;i++){
leftMax[i] = Math.max(leftMax[i-1],height[i]);
}
rightMax[height.length-1] =height[height.length-1];
for (int i=height.length-2;i>=0;i--){
rightMax[i] = Math.max(rightMax[i+1],height[i]);
}
for (int i=1;i<height.length-1;i++){
area += Math.min(leftMax[i],rightMax[i])-height[i];
}
return area;
//优化数组为左右指针,
// 因当前元素在找leftMax,rightMax时,由min(leftMax,rightMax)决定高度,
//故先用左右指针分别指向left,right,再记录leftMax,rightMax,左右指针分别向中间靠拢,过程中,若height[left]<height[right],操作左指针,否则操作右指针,以左指针为例,当前元素若小于leftMax,则计算当前元素储水面积,
// area = hight[leftMax] - hight[i] ,若当前元素若大于leftMax。则赋值leftMax = hight[i];
int left = 0;
int right = height.length-1;
int leftMax = 0;
int rightMax = 0;
int area =0;
while(left<right){
if (height[left]<height[right]){
if(height[left]<leftMax){
area += leftMax-height[left];
}else {
leftMax = height[left];
}
left++;
}else{
if(height[right]<rightMax){
area += rightMax-height[right];
}else {
rightMax = height[right];
}
right--;
}
}
return area;
}
}