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

思路

这题是字节跳动的社招题目。出现频率非常高。
分析题意,我们发现我们要找的肯定是两个比较高的中间有个比较低的,这样才能存水。我们可以先找到最高的柱子,用它来兜底。记住这个最高的柱子的坐标。
我们从左往右,从0遍历到这个坐标。设置一个lastHeight代表当前边界(另一个边界就是最高的柱子)的高度(边界嘛,肯定是遍历到此为止最高的那个柱子)。 如果柱子高度比边界要高,则更新边界。 如果柱子高度比边界低,则可以存水。
然后从右往左如法炮制。(方法一)

方法一虽然简单易懂,但是代码太多了。仔细想了一下,这题不就是:我们需要不断的找合适的边界,把边界内的符合条件的柱子都装上水吗??? 我们方法一中已经发现了,从左到右遍历时,左边界要从小到大,遇到比左边界大的,就更新左边界。 以此类推。
我们可以设置一个单调栈(学名是这样的)。这个栈里存储了单调不增的序列,如果待入栈元素大于栈顶元素,则证明遇到了右边界,可以出栈,计算水量;为了更新左边界的待选择值,我们计算完了之后要把这个值入栈。 否则,没有遇到右边界,入栈。
为什么要存储单调不增的序列呢?因为这题要有左右边界兜着才有意义。
(方法二)

方法一

Java版

public int trap1(int[] height) {
        int maxHeight = 0;
        int maxHeightIndex = 0;
        for(int i = 0; i < height.length; i++){
            if(height[i] > maxHeight){
                maxHeight = height[i];
                maxHeightIndex = i;
            }
        }
        int lastHeight = 0;
        int res = 0;
        for(int i = 0; i < maxHeightIndex; i++){
            if(lastHeight > height[i]){
                res += lastHeight - height[i];
            }else{
                lastHeight = height[i];
            }
        }
        lastHeight = 0;
        for(int i = height.length-1; i > maxHeightIndex; i--){
            if(lastHeight > height[i]){
                res += lastHeight - height[i];
            }else{
                lastHeight = height[i];
            }
        }
        return res;
    }

方法二

Java版

public int trap(int[] height){
        Stack<Integer> singleStack = new Stack<>();
        int res = 0;
        for(int i = 0; i < height.length; i++){
            // 右边界
            int rightBorder = i;
            while(!(singleStack.isEmpty()) && height[singleStack.peek()] < height[i]){
                // 当前的柱子
                int curIndex = singleStack.pop();
                // 相等的可以一次弹出
                while(!singleStack.isEmpty() && height[curIndex] == height[singleStack.peek()]){
                    singleStack.pop();
                }
                // 找左边界
                if(!singleStack.isEmpty()){
                    int leftBorder = singleStack.peek();
                    // 当前柱子的存储水量等于左右边界的较小值与柱子宽度的乘积
                    // 为什么不是1?因为有可能刚才弹出去很多相同高度的柱子
                    res += (Math.min(height[leftBorder],height[rightBorder])-height[curIndex])*(rightBorder-leftBorder-1);
                }
            }
            // 把右边界加入边界池
            singleStack.add(i);
        }
        return res;
    }
发布了464 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_41687289/article/details/105312661