LeetCode-42. 接雨水

128 阅读5分钟

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

注:本文章使用的代码为 Golang

官方示例图:

func trap(height []int) int {

}

题解

本题给了一个数组,数组的元素是柱子的高度。需要我们把积水量求出来

画图

使用图码结合,慢慢的把图变成代码,题目已经给了示例height,干脆直接用使用数组,于是得到下图

模拟过程

先不必写代码,观察 题目想要求 积水量 。积水量是如何来的?从图可以观察到,想要有积水量,就必须要有 “凹槽”。

凹槽

凹槽 又是如何来的?很简单,只要当前柱子(注意:这个柱子的高度可以是0)的左边和右边有高于自己的,就产生了高度差 从而出现凹槽。

左右高度比自己高

有凹槽产生了积水量,当左右边高度相同时,积水量就是 左右任意一个高度 - 自己高度。但是如果左右高度并不相同呢?

积水量

可以发现,当左右高度参差不齐时,当前柱子的积水量于自己,左边第一根 和 右边第一根是无关的。

这个 积水量 3 是怎么来的? 我们都听说过木桶效应,一个水桶无论有多高,它盛水的高度取决于其中最低的那块木板。并且发现,如果一直下雨,1、2、3会装满水,水会从 0号柱子上满出。 这里 2号比作水桶里的水,那么木板就是 0和4号柱子,因为他们都是两侧最高的柱子。

2号柱子 积水量 = (左右最高的柱子中)最矮的那颗柱子高度 - 当前柱子高度 = 4 - 1 = 3

分析总结

回过头看看积水过程中,我们做了什么?提取关键字。

  1. 当前柱子高度
  2. 第 i 根柱子,左边最高的柱子高度
  3. 第 i 根柱子,右边最高的柱子高度
  4. 左边和右边最高柱子高度中的最低那一根高度
  5. 积水量

最后根据统计出每根柱子积水量,积水量 = (左右最高的柱子中)最矮的那颗柱子高度 - 当前柱子高度。再把所有柱子积水量加起来 就是ans

尝试转成代码逻辑

转成代码

根据刚才的过程:

  • 当前柱子高度
  • 第 i 根柱子,左边最高的柱子高度
  • 第 i 根柱子,右边最高的柱子高度
  • 左边和右边最高柱子高度中的最低那一根高度
  • 积水量

当前柱子高度

当前柱子的高度,也就是第 i 根柱子的高度。遍历一遍 题目的height,在取值就可以。

也就是代码: height[i]

左边最高的柱子高度

第0根柱子,左边最高柱子高度是多少?第 1 根柱子,左边最高柱子高度又是多少?.....第 i 根柱子,.....

可以发现每颗柱子 0、1、2.....都需要统计,那么这时候就需要一个相同长度的数组 n := len(height),来保存max高度信息产生代码,即:

left := make([]int , n)

统计每根柱子左边最高的柱子高度,需要注意的是 第 0 根柱子,左边没有柱子,也就是它左边最高高度为0 ,所以统计时,从 1号柱子开始统计即可。

for i := 1; i < len(height); i++{
    left[i] = max(left[i - 1],height[i - 1]
}

右边最高的柱子高度

右边的统计和左边同理。即:

right := make([]int,n)

同样统计每根柱子右边最高的柱子高度时,最后一根,也就是序号 n-1 号的右边是没有柱子的,也就是0,所以直接从 n-2 开始统计即可。

for i := n-2; i > 0; i-- {
    right[i] = max(right[i + 1],height[i + 1])
}

左边和右边最高柱子高度中的最低那一根高度

这个好得出,一比一下就可以了。

short := min(left[i],right[i])

积水量

积水量 = (左右最高的柱子中)最矮的那颗柱子高度 - 当前柱子高度,那么所有积水量的和为:

ans = short - height[i]

完整代码

func trap(height []int) int {
    //定义答案
    ans := 0
    // 获取长度,简洁代码
    n := len(height)
    // 用来保存 左右柱子最大高度
    left := make([]int,n)
    right := make([]int,n)

    // 统计第 i 根左边最大高度柱子
    for i := 1; i < n; i++ {
        left[i] = max(left[i - 1],height[i - 1])
    }

      // 统计第 i 根右边最大高度柱子
    for i := n - 2; i > 0; i-- {
        right[i] = max(right[i + 1],height[i + 1])
    }

    for i := 0; i < n; i++ {
        short := min(left[i],right[i])
        if short > height[i] {
            ans += short - height[i]
        }
    }
    return ans
}

func max(a,b int) int{
    if a > b {
        return a
    }
    return b
}

func min(a,b int) int {
    if a > b {
        return b
    }
    return a
}

时空复杂度

时间复杂度:O(n)

在有三个主要的循环。第一个循环用于计算每个位置的左边最大柱子高度,第二个循环用于计算每个位置的右边最大柱子高度,第三个循环用于计算积水量并累加到答案变量中。由于这三个循环都是线性的,因此时间复杂度为 O(n),其中 n 是输入数组 height 的长度。

空间复杂度:O(n)

除了输入数组 height 外,还定义了两个大小为 n 的数组 leftright,以及一个常量大小的变量 ans。因此,空间复杂度主要取决于额外的数组空间,即 O(n)。同时,由于没有使用递归或其他额外的数据结构,空间复杂度不会有额外的因素。