力扣HOT100之双指针:42. 接雨水

这道题在之前刷代码随想录的时候做过,果然现在又不记得了,当时是用单调栈做的,现在忘得一干二净,难绷。。。

单调栈的思路不是本文的重点,单调栈的思路可以参考我的这篇博客

这道题我用双指针想不出来,然后去看灵神的题解了,学到了一种用前后缀解决的新办法,有必要记录一下。

前后缀解法

我们可以认为每一个柱子都与相邻的两根柱子组成一个水桶,当中间的柱子比两边的柱子低时,则可以留住雨水,当然,当多个柱子排列在一起时,只需要满足当前柱子的两侧(不一定是相邻的柱子)都有比自己高的柱子时,就能留住雨水,而当前位置能留住多少雨水,则取决于两侧较短的柱子与当前柱子之间的高度差,因此我们需要额外定义两个数组,一个用来存储每一个柱子及其前面的柱子中的最大高度,prefix[i]则表示从下标为0到下标为i的柱子中的最大高度,同理,定义另一个数组用来存储每一个柱子及其后面的柱子中的最大高度,suffix[i]则表示从下标为iheight[height.size() - 1]的柱子中的最大高度。然后从前往后遍历记录prefix[i],从后往前记录suffix[i].

然后再从前往后遍历一遍柱子,只有满足height[i] < prefix[i] && height[i] < suffix[i]时才能留住雨水,该柱子处能留住的雨水量为min(prefix[i], suffix[i]) - height[i],单调栈是横着计算雨水量,而前后缀法则是竖着计算雨水量,非常生动贴切。

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int result = 0;
        vector<int> prefix(height.size(), 0);   //用来记录前缀(从下标为0到i的最大高度)
        vector<int> suffix(height.size(), 0);   //用来记录后缀(从下标为i到height.size() - 1的最大高度)
        prefix[0] = height[0];
        suffix[height.size() - 1] = height[height.size() - 1];
        for(int i = 1; i < height.size(); i++)   //记录每一个位置的最大前缀
            prefix[i] = max(prefix[i - 1], height[i]);
        for(int i = height.size() - 2; i >= 0; i--)  //记录每一个位置的最大后缀
            suffix[i] = max(suffix[i + 1], height[i]);
        for(int i = 0; i < height.size(); i++){  //记录每一个位置上的水桶所装的水
            if(height[i] < prefix[i] && height[i] < suffix[i])
                result += (min(prefix[i], suffix[i]) - height[i]);
        }
        return result;
    }
};

双指针法

双指针法需要以前后缀法为基础,实际上是对前后缀法的一种优化,我们可以定义左右指针相向移动,左指针右移的过程中不断更新最大前缀,用一个变量维护即可,右指针左移的过程中同样用一个变量维护最大后缀,我们需要知道这样一条性质:无论是height[left]还是height[right],都满足max_pre >= height[left]max_suf >= height[right],这就意味着对于height[left]处的柱子来说,只要右侧的最大后缀出现比最大前缀高的情况,就满足了接水的条件,即可开始计算接水量,最坏情况是最大前缀恰好等于 height[left],这个时候算出来的接水量为0,当height[left]处的柱子接完水后,左指针应当向右移。右边指针也同理,只要出现最大前缀大于最大后缀时,就开始计算height[right]处的接水量,最坏情况是接不到水,计算完以后右指针左移。

当左右指针相遇时,left = right,注意,此时height[left](或height[right])处仍然有可能接到水,所以这个时候还不能退出循环,注意循环终止条件的设置。

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int result = 0;
        int left = 0;
        int right = height.size() - 1;
        int max_pre = 0;  //记录最大前缀
        int max_suf = 0;  //记录最大后缀          
        while(left <= right){
            max_pre = max(max_pre, height[left]);  //更新最大前缀,保证最大前缀至少>=height[left]
            max_suf = max(max_suf, height[right]); //更新最大后缀,保证最大后缀至少>=height[right]
            if(max_pre < max_suf){ //最大前缀小于最大后缀
                result += (max_pre - height[left]);
                left++;
            }
            else{  //最大前缀大于等于最大后缀
                result += (max_suf - height[right]);
                right--;
            }
        }
        return result;
    }
};
相关推荐
_OP_CHEN20 分钟前
【算法基础篇】(五十七)线性代数之矩阵乘法从入门到实战:手撕模板 + 真题详解
线性代数·算法·矩阵·蓝桥杯·c/c++·矩阵乘法·acm/icpc
天天爱吃肉821825 分钟前
【跨界封神|周杰伦×王传福(陶晶莹主持):音乐创作与新能源NVH测试,底层逻辑竟完全同源!(新人必看入行指南)】
python·嵌入式硬件·算法·汽车
im_AMBER25 分钟前
Leetcode 114 链表中的下一个更大节点 | 删除排序链表中的重复元素 II
算法·leetcode
xhbaitxl38 分钟前
算法学习day38-动态规划
学习·算法·动态规划
多恩Stone38 分钟前
【3D AICG 系列-6】OmniPart 训练流程梳理
人工智能·pytorch·算法·3d·aigc
历程里程碑41 分钟前
普通数组----轮转数组
java·数据结构·c++·算法·spring·leetcode·eclipse
pp起床42 分钟前
贪心算法 | part02
算法·leetcode·贪心算法
sin_hielo42 分钟前
leetcode 1653
数据结构·算法·leetcode
2501_9011478344 分钟前
面试必看:优势洗牌
笔记·学习·算法·面试·职场和发展
YuTaoShao1 小时前
【LeetCode 每日一题】3634. 使数组平衡的最少移除数目——(解法二)排序 + 二分查找
数据结构·算法·leetcode