【单调栈】接雨水


在这里插入图片描述

双指针

每一列雨水的高度,取决于该列 min(左侧最高的柱子高度,右侧最高的柱子高度) - 当前柱子高度

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int ans = 0;
        for(int i = 1; i < n - 1; i++){
            int l = 0;
            for(int j = i - 1; j >= 0; j--){
                if(height[j] > l) l = height[j];
            }
            int r = 0;
            for(int j = i + 1; j < n; j++){
                if(height[j] > r) r = height[j]; 
            }
            if(min(l, r) > height[i]) ans += (min(l, r) - height[i]);
        }
        return ans;
    }
};

在这里插入图片描述

动态规划

我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。

当前列雨水面积:min(左边柱子的最高高度,右边柱子的最高高度) - 当前柱子高度

我们把数组遍历两遍,通过动态规划,就可以得到当前第 i 列,左侧最高的柱子lmax[i]和右侧最高的柱子rmax[i]

lmax[i] = max(height[i-1], lmax[i-1])
rmax[i] = max(height[i+1], rmax[i+1])

在这里插入图片描述

int trap(vector<int>& height) {
    int n = height.size();
    vector<int> lmax(n, 0);
    vector<int> rmax(n, 0);
    for(int i = 1; i < n; i++){
        lmax[i] = max(lmax[i-1], height[i-1]);
    }
    for(int i = n - 2; i >= 0; i--){
        rmax[i] = max(rmax[i+1], height[i+1]);
    }

    int ans = 0;
    for(int i = 1; i < n - 1; i++){
    	// 第0列和最后一列不接雨水
        if(min(lmax[i], rmax[i]) > height[i]) ans += (min(lmax[i], rmax[i]) - height[i]); 
    }
    return ans;
}

单调栈

单调栈是按照行方向来计算雨水,栈中按照栈底到栈顶,高度降序的方式存放对应的下标,一旦遍历到的元素height[i]大于栈顶元素了,说明就有凹槽,可以用来接雨水了。

  1. 当前元素height[i] < 栈顶元素height[st.top()],当前元素入栈
  2. 当前元素height[i] == 栈顶元素height[st.top()],栈顶元素出栈后,当前元素入栈,更新下标。因为栈内元素作为容器的左壁,接雨水时按照最靠右的元素作为容器左壁,所以需要更新值相同的下标
  3. 当前元素height[i] > 栈顶元素height[st.top()],有凹槽,可以用来接雨水

接雨水时,以当前元素height[i]为右侧,当前栈顶元素height[mid]为底,出栈后的栈顶元素为左侧height[st.top()],形成的凹槽接雨水。若当前元素height[i]一直大于栈顶元素,则一直以当前元素height[i]为右侧形成的凹槽接雨水

在这里插入图片描述
例如当前元素height[i]为5时,当前元素是一直大于栈顶元素的,需要不断计算以不同的栈顶元素为底的容器接的雨水,直到栈顶元素不小于当前元素height[i]为止。图中蓝色的方框就是以不同的容器底部,相同的容器右侧接的雨水,

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        stack<int> st;
        int ans = 0;
        st.push(0);
        for(int i = 1; i < n; i++){
            if(height[i] < height[st.top()]){
                st.push(i);
            }else if(height[i] == height[st.top()]){
                st.pop();     // 其实可以不加,直接压栈也可以,计算面积时取出的也是后加入的下标
                st.push(i);
            }else{
            	// while循环把栈内小于当前元素的都处理完
                while(!st.empty() && height[i] > height[st.top()]){
                	// // 以mid为底,st.top()和i为两侧的容器接雨水
                    int mid = st.top();
                    st.pop();
                    if(!st.empty()){
                    	// 栈不空,容器才有左侧,才能接雨水
                        int w = i - st.top() - 1;
                        int h = min(height[i], height[st.top()]) - height[mid];
                        ans += h * w;
                    }
                }
                st.push(i);
            }
        }
        return ans;
    }
};

精简代码,合并处理情况1、2、3

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        stack<int> st;
        int ans = 0;
        st.push(0);
        for(int i = 1; i < n; i++){
            while(!st.empty() && height[i] > height[st.top()]){
                int mid = st.top();
                st.pop();
                if(!st.empty()){
                    int w = i - st.top() - 1;
                    int h = min(height[i], height[st.top()]) - height[mid];
                    ans += h * w;
                }
            }
            st.push(i);   // 情况1、2就不进入while循环,直接执行push
        }
        return ans;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值