leetcode 42.接雨水

思路一:相向双指针

这里为什么用到双指针?我们可以通过题目来看一下,因为我们需要装水,所以需要一种形状为“凹”类似的容器来装水。这样的话,我们需要讨论的无非就是两边的容器壁大小了。

如果你想着用相同方向的双指针进行遍历,有几种特殊情况你需要进行细分的讨论:当遍历到最后的时候高度依然小于左边的时候(当然假设我们以左边的容器壁为主)我们不得不进行讨论刚刚已经遍历过的容器壁是否能够装水,这样的话还需要再次移动左边指针再遍历一次,会增加时间复杂度,很麻烦。

这里就是一种新思路,相向,也就是把左指针放到最左边,右指针放到最右边,然后进行判断。除了这样,我们需要维护最左边的最高容器壁,和最右边的最高容器壁。因为如果我们移动指针,这个时候一边容器壁反而降低了,这样就不可能能够接到水(如果遇到凹坑,也就是高度为0),所以我们两边都需要维护这个容器壁高度。为什么又说是最高呢?我们遍历其实就是按照容器壁从小到大高度时一共能够盛多少水的,其实就是一步一步增高,一个一个判断水的容量,这样不仅清晰,而且能够不重不漏的计算出来水容量。(是不是有点动态规划转移状态那个味道呢?)

假设我们左边的容器壁当前并没有右边的容器壁高,那么我们这个时候需要看一看中间是否能够构成凹形。如果能,我们计入水容量(通过左边的最大容器壁-当前左指针指向的容器壁高度),不能,我们看看是不是跟当前最大容器壁的高度更大,更大就更新,否则不然。换做是右边也是一样的。

class Solution {
    public int trap(int[] height) {
        int left=0;
        int right=height.length-1;
        int leftMax=height[left];
        int rightMax=height[right];
        int res=0;
        while(left<=right){
            if(leftMax<rightMax){
                res+=leftMax-height[left];
                left++;
                if(left<=height.length-1){
                    leftMax=Math.max(leftMax,height[left]);
                }
            }
            else{
                res+=rightMax-height[right];
                right--;
                if(right>=0){
                    rightMax=Math.max(rightMax,height[right]);
                }
            }
        }
        return res;
    }
}

思路二:单调栈

其实一开始并不知道为什么能够用这个方法进行解答。经过上一个方法的思路提取,我们知道一个根本的东西:就是我们一直在两边维护着最高的容器壁这一点。对于没有最高容器壁高度的部分,我们选择竖向计算水的容量。这里就不同于上一种方法了,我们这时开始横向的计算水容量,维护容器壁的高度也用单调栈维护。

这里用了非递增的单调栈,当我们遇见比栈顶高的容器壁的时候,就出栈然后计算当前横向的水容量,直到遇见栈内比它大的容器壁为止,不然就一直退栈计算容量,一直到栈为空的时候或者遍历完全部高度。

注意:我们在计算高度的时候是需要先出栈,获得出栈元素,然后再提取出来当前栈顶元素和当前比出栈元素大的高度,比较这两个高度谁小。为什么?因为可能这个元素比刚刚出栈的元素对应的容器壁高度大,但是不一定比后面的栈元素对应的容器壁的高度大,所以需要额外判断一下。至于横向的长度怎么算,直接用当前的元素坐标-栈内存储的栈顶元素就行。(栈内维护的是数组的坐标值)。

class Solution {
    public int trap(int[] height) {
        Deque<Integer>stack=new LinkedList();
        int res=0;
        for(int i=0;i<height.length;i++){
            while(!stack.isEmpty()&&height[stack.peek()]<height[i]){
                int num=stack.pop();
                if(stack.isEmpty())break;
                res+=(Math.min(height[stack.peek()],height[i])-height[num])*(i-stack.peek()-1);
            }
            stack.push(i);
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值