给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 力扣(LeetCode)—— 42. 接雨水
注:本文章使用的代码为 Golang
官方示例图:
func trap(height []int) int {
}
题解
本题给了一个数组,数组的元素是柱子的高度。需要我们把积水量求出来
画图
使用图码结合,慢慢的把图变成代码,题目已经给了示例height,干脆直接用使用数组,于是得到下图
模拟过程
先不必写代码,观察 题目想要求 积水量 。积水量是如何来的?从图可以观察到,想要有积水量,就必须要有 “凹槽”。
凹槽
凹槽 又是如何来的?很简单,只要当前柱子(注意:这个柱子的高度可以是0)的左边和右边有高于自己的,就产生了高度差 从而出现凹槽。
左右高度比自己高
有凹槽产生了积水量,当左右边高度相同时,积水量就是 左右任意一个高度 - 自己高度。但是如果左右高度并不相同呢?
积水量
可以发现,当左右高度参差不齐时,当前柱子的积水量于自己,左边第一根 和 右边第一根是无关的。
这个 积水量 3 是怎么来的? 我们都听说过木桶效应,一个水桶无论有多高,它盛水的高度取决于其中最低的那块木板。并且发现,如果一直下雨,1、2、3会装满水,水会从 0号柱子上满出。 这里 2号比作水桶里的水,那么木板就是 0和4号柱子,因为他们都是两侧最高的柱子。
2号柱子 积水量 = (左右最高的柱子中)最矮的那颗柱子高度 - 当前柱子高度 = 4 - 1 = 3
分析总结
回过头看看积水过程中,我们做了什么?提取关键字。
- 当前柱子高度
- 第 i 根柱子,左边最高的柱子高度
- 第 i 根柱子,右边最高的柱子高度
- 左边和右边最高柱子高度中的最低那一根高度
- 积水量
最后根据统计出每根柱子积水量,积水量 = (左右最高的柱子中)最矮的那颗柱子高度 - 当前柱子高度。再把所有柱子积水量加起来 就是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 的数组 left 和 right,以及一个常量大小的变量 ans。因此,空间复杂度主要取决于额外的数组空间,即 O(n)。同时,由于没有使用递归或其他额外的数据结构,空间复杂度不会有额外的因素。