「青训营 X 码上掘金」[单调栈]统计青豆数量

185 阅读3分钟

当青训营遇上码上掘金

题目

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

攒青豆.png

输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路

思路:我们可以从下往上,按行(层)统计豆子的数量。

以上图为例,我们可以按照以下方式遍历——依照下图的顺序统计青豆的面积。

Pasted image 20230211230754.png

从例子中总结规律

  1. 当我们找到一个类似凹字的结构,我们就可以统计以下这个图形中的豆子面积。
  2. 凹字的结构的发现:
    1. 我们按序遍历输入数组将柱子的编号放入容器中,如果当前遍历的高度比之前的小那么就一直放入容器
    2. 如果当前的值 height[i] 比我们上一次放入容器的值还要小,那么我们可以增加统计青豆数量
      1. 以区域(1) 为例,当前容器 [0,1] , 我们取出容器尾的 1
        1. 这时我们需要判断我们取出的柱子是不是最左侧的
          1. 最左侧:不能放青豆
          2. 非最左侧:这时候 计算矩形的面积= 两侧最高可以兜住的高度* 两侧的距离=(2-0) *(2-0-1)=2,并且将结果追加到 最终结果 res
            • 注意:两端最高可以兜住的高度是两侧更低者高度-我们最近取出的柱子的高度。
      2. 以区域(3)为例,当前容器 [0,2] , 我们取出容器尾的 2
        1. 此时取出的柱子不是最左侧的
          1. 计算矩形的面积= (4-2)*(4-0-1)=6 ,将结果追加到最终结果 res
    3. 反之,我们一直在容器末尾追加新的柱子的标号
  3. 获得结果。

到了这一步,题解已经呼之欲出,但是问题是,我们没有现成的容器,只能自己实现。这个容器其实就是大名鼎鼎的 单调栈

题解 :通过单调栈解决

核心代码

class Solution {

    public int trap(int[] height) {

        int res=0;

        int n=height.length;

        if (n <= 2return 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;

    }

}

更多

核心代码调用测试已经发布到 ==码上掘金==。欢迎围观/ 拍砖 :)