• [LeetCode 42.] 接雨水


    LeetCode 42. 接雨水

    题目描述

    给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

    示例 1:

    输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
    输出:6
    解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

    示例 2:

    输入:height = [4,2,0,3,2,5]
    输出:9

    提示:

    • n == height.length
    • 0 <= n <= 3 * 104
    • 0 <= height[i] <= 105

    解题思路一:纵向,关注每一个条形的注水量

    这种思路比较直观,每一个条形能注水的多少,取决于当前条形高度,以及左侧最高条形高度和右侧最高条形高度。

    注意,如果每个条形都现场查找左右两侧最大高度,二层循环下的时间按复杂度是 O(N^2)。

    动态规划

    我们可以使用数组保存两侧最高条形高度这个“状态值”,然后直接查表即可。

    /*
     * @lc app=leetcode id=42 lang=cpp
     *
     * [42] Trapping Rain Water
     */
    
    // @lc code=start
    class Solution {
    public:
        int trap(vector<int>& height) {
            if (height.empty()) return 0;
            int n = height.size();
            vector<int> left_max(n), right_max(n);
            left_max[0] = height[0];
            for (int i = 1; i < n; i++) {
                left_max[i] = max(height[i], left_max[i - 1]);
            }
            right_max[n - 1] = height[n - 1];
            for (int i = n - 2; i >= 0; i--) {
                right_max[i] = max(height[i], right_max[i + 1]);
            }
            int res = 0;
            for (int i = 1; i < n - 1; i++) {
                res += min(left_max[i], right_max[i]) - height[i];
            }
            return res;
        }
    };
    // @lc code=end
    

    双指针

    同样是利用两侧最高条形来计算,另一种方法是我们不用单向扫描,而是改用双向扫描算法,也就是双指针的思路。我们从两端选取能确定蓄水量的条形,并向中间相向推进。

    /*
     * @lc app=leetcode id=42 lang=cpp
     *
     * [42] Trapping Rain Water
     */
    
    // @lc code=start
    class Solution {
    public:
        int trap(vector<int>& height) {
            int left = 0, right = height.size() - 1;
            int left_max = 0, right_max = 0;
            int res = 0;
            while (left < right) {
                if (height[left] < height[right]) {
                    if (left_max <= height[left]) {
                        left_max = height[left];
                    } else {
                        res += (left_max - height[left]);
                    }
                    ++left;
                } else {
                    if (right_max <= height[right]) {
                        right_max = height[right];
                    } else {
                        res += (right_max - height[right]);
                    }
                    --right;
                }
            }
            return res;
        }
    };
    // @lc code=end
    

    解题思路二:横向,关注每次涨水的水量

    思路是这样的:我们不断增加新的条形,这样凹槽中的水位可能就要不断上涨。只需要计算出每次上涨的水量,并累加求和即可。

    单调栈

    这种方案用的是单调栈。入栈 —— 保存状态;出栈 —— 计算收益。

    右边新增的条形更短,则入栈保存状态;新条形更长,则触发涨水条件,出栈计算水量。

    /*
     * @lc app=leetcode id=42 lang=cpp
     *
     * [42] Trapping Rain Water
     */
    
    // @lc code=start
    class Solution {
    public:
        int trap(vector<int>& height) {
            int water = 0;
            deque<int> q; // 单调队列,存下标
            for (int i=0; i<height.size(); i++) {
                // 比左边矮没事,比左边高就要涨水了
                while (!q.empty() && height[i] > height[q.back()]) {
                    int low = height[q.back()]; // 原水位
                    q.pop_back();
                    if(!q.empty()) {
                        int w = i - q.back() - 1;
                        int h = min(height[i],  height[q.back()]) - low; // 增加的水位高度
                        water += w * h;
                    } // 原栈顶决定了低水位,新栈顶和新条形决定了高水位和宽度
                }
                q.push_back(i);
            }
            return water;
        }
    };
    // @lc code=end
    
  • 相关阅读:
    vs code 使用小技巧
    数组22组合
    js--arTemplate引擎
    JAVA -简要记录maven的安装与环境变量的配置
    JAVA -简要记录jdk的安装与环境变量的配置
    浅谈“复制粘贴”对于程序员的伤害
    C#中 IndexOf的使用
    C# Substring函数的总结
    C# 还原Nuget包失败的解决方法
    C# 未能找到类型或命名空间名称“XXXX”(是否缺少 using 指令或程序集引用?)解决方案
  • 原文地址:https://www.cnblogs.com/zhcpku/p/14450892.html
Copyright © 2020-2023  润新知