算法最优解 算法最优解
首页
目录
赞助
GitHub (opens new window)
首页
目录
赞助
GitHub (opens new window)
  • 数据结构基础

  • 数组

    • 两数之和
    • 买卖股票的最佳时机
    • 存在重复元素
    • 除自身以外数组的乘积
    • 最大子数组和
    • 和为 K 的子数组
    • 乘积最大子数组
    • 轮转数组
    • 寻找旋转排序数组中的最小值
    • 搜索旋转排序数组
    • 盛最多水的容器
    • 接雨水
      • 题目描述
      • 思路解析
      • c++代码
      • java代码
      • python代码
      • 复杂度分析
    • 三数之和
    • 移动零
  • 位运算

  • 动态规划

  • 图

  • 区间

  • 链表

  • 矩阵

  • 字符串

  • 树

  • 堆

  • 逻辑思维

  • 目录
  • 数组
华南溜达虎
2024-07-10
目录

接雨水

题目链接: https://leetcode.cn/problems/trapping-rain-water/

# LeetCode 42. 接雨水

# 题目描述

给定 n 个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。 

# 思路解析

采用逐列计算,计算每列最多能接雨水量,然后把每列能接的雨水量加起来,就可以得到所有柱子总的接雨水量。

那么对于第i列,要怎样计算它能接的雨水量呢?

我们可以找出0 ~ i列最高的柱子,记为maxL,然后找到i ~ n列最高的柱子,记为maxR,maxL和maxR分别是第i列的左右边界,因为第i列能接的雨水量是由较短的那个边界决定的,所以min(maxL, maxR) - height[i]就是第i列能接的雨水量,下图中的红色区域就是第i列可以接的雨水量。

我们以上面的数组height = [0,1,0,2,1,0,1,3,2,1,2,1]为例,来看一下整个接雨水的过程。

  1. 从左到右遍历数组,来计算0 ~ i列最高的柱子maxL,即第i列的左边界。
height 0 1 0 2 1 0 1 3 2 1 2 1
maxL 0 1 1 2 2 2 2 3 3 3 3 3
  1. 从右到左遍历数组, 来计算n ~ i列最高的柱子maxR,即第i列的右边界。。
height 0 1 0 2 1 0 1 3 2 1 2 1
maxL 0 1 1 2 2 2 2 3 3 3 3 3
maxR 3 3 3 3 3 3 3 3 2 2 2 1
  1. 计算min(maxL, maxR),即较短的边界长度。
height 0 1 0 2 1 0 1 3 2 1 2 1
maxL 0 1 1 2 2 2 2 3 3 3 3 3
maxR 3 3 3 3 3 3 3 3 2 2 2 1
min(maxL, maxR) 0 1 1 2 2 2 2 3 2 2 2 1
  1. 计算每一列能接的雨水量min(maxL, maxR) - height[i],即较短的边界减去第i列柱子本身的高度。
height 0 1 0 2 1 0 1 3 2 1 2 1
maxL 0 1 1 2 2 2 2 3 3 3 3 3
maxR 3 3 3 3 3 3 3 3 2 2 2 1
min(maxL, maxR) 0 1 1 2 2 2 2 3 2 2 2 1
min(maxL, maxR)-height[i] 0 0 1 0 1 2 1 0 0 1 0 0
  1. 把所有的min(maxL, maxR)-height[i]>0的值加在一起就是能接雨水的总量。

上面的整个过程需要借助额外的空间,空间复杂度为O(n),其中n为数组中元素的个数。

我们可以使用双指针法对上面的步骤进行优化。

定义L指向数组的第一个元素,R指向数组的最后一个元素,初始化左右边界maxL = height[L], maxR = height[R]。

因为每一列接到雨水的量是由较短的边界决定的,所以一旦某一列左右边界的较短边界确定了,我们就可以计算对应列可接的雨水量,每次计算左右边界maxL和maxR中较小值对应的列就可以达到这个目的。

  1. 如果maxL < maxR,此时L对应列的较短边界是确定的,利用公式maxL - height[L]计算L对应列可以接到雨水的量,大于0就累加到结果中,并将L向右移动一步++L,更新maxL = max(maxL, height[L])。

  2. 如果maxL >= maxR,此时R对应列的较短边界是确定的,利用公式maxR - height[R]计算R对应列可以接到雨水的量,大于0就累加到结果中,并将R向左移动一步--R,更新maxR = max(maxR, height[R])。

# c++代码

class Solution {
public:
    int trap(vector<int>& height) {
        int res = 0;
        //定义两个指针
        int left = 0, right = height.size() - 1;
        //定义待计算列的左右边界
        int maxL = height[left], maxR = height[right];
        while (left <= right) {
            //计算较小边界对应指针指向的列接雨水的量
            if (maxL < maxR) {
                int temp = maxL - height[left];
                if (temp > 0) 
                    res += temp;
                ++left;
                //防止越界访问
                if (left < height.size())
                    maxL = max(maxL, height[left]);
            } else {
                int temp = maxR - height[right];
                if (temp > 0) 
                    res += temp;
                --right;
                //防止越界访问
                if (right >= 0)
                    maxR = max(maxR, height[right]);
            }
        }
        return res;
    }
};

# java代码

class Solution {
    public int trap(int[] height) {
        int res = 0;
        //定义两个指针
        int left = 0, right = height.length - 1;
        //定义待计算列的左右边界
        int maxL = height[left], maxR = height[right];
        while (left <= right) {
            //计算较小边界对应指针指向的列接雨水的量
            if (maxL < maxR) {
                int temp = maxL - height[left];
                if (temp > 0) {
                    res += temp;
                }
                left++;
                //防止越界访问
                if (left < height.length) {
                    maxL = Math.max(maxL, height[left]);
                }
            } else {
                int temp = maxR - height[right];
                if (temp > 0) {
                    res += temp;
                }
                right--;
                //防止越界访问
                if (right >= 0) {
                    maxR = Math.max(maxR, height[right]);
                }
            }
        }
        
        return res;
    }
}

# python代码

class Solution:
    def trap(self, height: List[int]) -> int:
        res = 0
        #定义两个指针
        left, right = 0, len(height) - 1
        #定义待计算列的左右边界
        maxL, maxR = height[left], height[right]
        while left <= right:
            #计算较小边界对应指针指向的列接雨水的量
            if maxL < maxR:
                temp = maxL - height[left]
                if temp > 0:
                    res += temp
                left += 1
                #防止越界访问
                if left < len(height):
                    maxL = max(maxL, height[left])
            else:
                temp = maxR - height[right]
                if temp > 0:
                    res += temp
                right -= 1
                #防止越界访问
                if right >= 0:
                    maxR = max(maxR, height[right])
        
        return res

# 复杂度分析

时间复杂度: 只需要遍历一遍数组,所以时间复杂度为O(n),其中n为数组的大小。

空间复杂度: 只用到两个指针,所以空间复杂度为O(1)。

上次更新: 2024/07/14, 18:20:07
盛最多水的容器
三数之和

← 盛最多水的容器 三数之和→

Theme by Vdoing | Copyright © 2024-2024 华南溜达虎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式