leetcode【困难】42、接雨水

本文详细介绍了四种不同的算法方法来解决柱状图中雨水填充问题,包括双指针、动态规划、单调栈和顺序模拟。每种方法都通过遍历柱子,寻找左右最高柱子,计算雨水体积。这些算法旨在优化时间复杂度和空间复杂度,以高效地求解问题。
摘要由CSDN通过智能技术生成

在这里插入图片描述

思路一:双指针

时间复杂度为O(n^2)。 空间复杂度为O(1)
在这里插入图片描述
列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。

列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。

列4 柱子的高度为1(以下用height表示)

那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。

列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。

(注意第一个柱子和最后一个柱子不接雨水)

class Solution {
    public int trap(int[] height) {
        int sum=0;
        //第一个柱子和最后一个柱子不接雨水
        for(int i=1;i<height.length-1;i++){
            int rh=height[i];//双指针找左右最高的柱子
            int lh=height[i];

            for(int r=i+1;r<height.length;r++){
                rh=Math.max(rh,height[r]);
            }
            for(int l=i-1;l>=0;l--){
                lh=Math.max(lh,height[l]);
            }
            int h=Math.min(lh,rh)-height[i];
            if(h>0) sum+=h;
        }
        return sum;
    }
}

思路二:动态规划

代替双指针,记录左右最高的柱子
每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)

从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);

从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);

class Solution {
    public int trap(int[] height) {
        int sum=0;
        int len=height.length;
        if(len<=2) return sum;
        int[] maxLeft=new int[len];
        int[] maxRight=new int[len];
        maxLeft[0]=height[0];
        maxRight[len-1]=height[len-1];

        for (int i = 1; i < len; i++) {
            maxLeft[i] = Math.max(height[i], maxLeft[i - 1]);
        }
        for (int i = len-2; i >= 0; i--) {
            maxRight[i] = Math.max(height[i], maxRight[i + 1]);
        }
        //遍历柱子,求和
        for(int i=1;i<len-1;i++){
            int temp=Math.min(maxLeft[i],maxRight[i])-height[i];
            if(temp>0) sum+=temp;
        }
        return sum;
    }
}

思路三:单调(递减)栈

在这里插入图片描述
当前柱子比栈顶高时,当前柱子就是栈顶柱子的【右端】,栈顶的前一个就是【左端】
栈里储存坐标,柱子高度单调递减

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

思路四:顺序模拟

遍历得到当前墙,如果比前一个墙高,加上与前一个墙之间的体积,并接着向前遍历,如果之前有比前一个墙更高的(比当前墙低),再算上增加的体积,最后弹出所有比当前墙低的,以后也不会用上了

注意:每遍历一个墙,要维护一个bottom,接满水的部分就相当于底部升高

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        stack = []  # 记录遍历过的矮墙
        for i, h in enumerate(height):
            if h == 0:
                continue
            bottom = 0  # 底部,如果之前的坑填满了雨水,就相当于底部升高了
            while stack:
                ii, hh = stack[-1]
                if h >= hh:  # 当前的墙比前面的高
                    ans += (i - ii - 1) * (hh - bottom)
                    bottom = hh
                    # 处理完当前墙的前一面墙
                    #可以接着向前遍历看又没更高的空间
                    #将比当前墙低的都弹出,以后也不会用到了
                    stack.pop()  
                else:  # 当前的墙比前面的矮
                    ans += (i - ii - 1) * (h - bottom)
                    break  # 不用再向前找更高的墙了
            stack.append((i, h))
        return ans
class Solution {
    public int trap(int[] height) {
        int sum=0;
        int len=height.length;
        Stack<Integer>stack=new Stack<>();
        for(int i=0;i<len;i++){
            if(height[i]==0) continue;
            int bottom=0;
            while(stack.size()!=0){
                int temp=stack.peek();
                if(height[temp]<=height[i]){
                    sum+=(i-temp-1)*(height[temp]-bottom);
                    bottom=height[temp];
                    stack.pop();
                }else{//不用再向前找更高的墙了,栈单调递减
                    sum+=(i-temp-1)*(height[i]-bottom);
                    break;
                }
            }
            stack.add(i);
        }
        return sum;
    }
}

改编

柱子接雨水,改为木板盛雨水
思路:

  1. 从左向右遍历,记录当前位置为止最高木板的下标,如果当前木板高于之前最高木板,加上这部分
  2. 如果最高木板不在最右边,还要再从右向左遍历,操作同上
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值