当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路
思路:我们可以从下往上,按行(层)统计豆子的数量。
以上图为例,我们可以按照以下方式遍历——依照下图的顺序统计青豆的面积。
从例子中总结规律
- 当我们找到一个类似凹字的结构,我们就可以统计以下这个图形中的豆子面积。
- 凹字的结构的发现:
- 我们按序遍历输入数组将柱子的编号放入容器中,如果当前遍历的高度比之前的小那么就一直放入容器
- 如果当前的值
height[i]比我们上一次放入容器的值还要小,那么我们可以增加统计青豆数量- 以区域(1) 为例,当前容器
[0,1], 我们取出容器尾的 1- 这时我们需要判断我们取出的柱子是不是最左侧的
- 最左侧:不能放青豆
- 非最左侧:这时候
计算矩形的面积= 两侧最高可以兜住的高度* 两侧的距离=(2-0) *(2-0-1)=2,并且将结果追加到 最终结果res。- 注意:两端最高可以兜住的高度是两侧更低者高度-我们最近取出的柱子的高度。
- 这时我们需要判断我们取出的柱子是不是最左侧的
- 以区域(3)为例,当前容器
[0,2], 我们取出容器尾的 2- 此时取出的柱子不是最左侧的
计算矩形的面积= (4-2)*(4-0-1)=6,将结果追加到最终结果res。
- 此时取出的柱子不是最左侧的
- 以区域(1) 为例,当前容器
- 反之,我们一直在容器末尾追加新的柱子的标号
- 获得结果。
到了这一步,题解已经呼之欲出,但是问题是,我们没有现成的容器,只能自己实现。这个容器其实就是大名鼎鼎的 单调栈 。
题解 :通过单调栈解决
核心代码
class Solution {
public int trap(int[] height) {
int res=0;
int n=height.length;
if (n <= 2) return 0;
Deque<Integer> stack=new ArrayDeque<>(n);//单调栈
for(int i=0;i<n;i++){
if(stack.isEmpty()){//为空,说明没有左边界,push
stack.addLast(i);
}else{//有左边界
while(!stack.isEmpty()){
int firstPeek=stack.peekLast();
if(height[i]>height[firstPeek]){//高于左侧
int poll=stack.pollLast();
if(stack.size()==0){//发现是最左侧,更新最左侧
stack.addLast(i);
break;
}
if(height[i]>=height[stack.peekLast()]){//高于左边界
//res += i到左边界的距离 * 左边界和i左侧的高度差
res+=(i-stack.peekLast()-1)*(height[stack.peekLast()]-height[poll]);
continue;//继续找,可能还没有到最左侧
}
if(height[i]<height[stack.peekLast()]){//低于左边界
//res += i到左边界的距离 * i和i左侧的高度差
res+=(i-stack.peekLast()-1)*(height[i]-height[poll]);
stack.addLast(i);//push i 现在可以看作i的高度作为“凹”字底部的高度
break;
}
}
if(height[i]<=height[firstPeek]){//小于等于左侧,满足入栈要求,push
stack.addLast(i);
break;
}
}
}
}
return res;
}
}
更多
核心代码调用测试已经发布到 ==码上掘金==。欢迎围观/ 拍砖 :)