题目背景
当青训营遇上码上掘金
-
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路分析
这道题目是一道典型的模拟题,和经典题目接雨水是一样的,其主要思路是模拟雨水从高处流到低处的过程,然后计算每一列能够容纳的雨水量,最后将所有列的雨水量相加得到总体积。
方法一是最直接的暴力解法,先求出每一列左边和右边的最高高度,然后对于每一列,计算其容纳的雨水量,然后将所有列的雨水量相加即可得到总体积。这种方法比较直观,但是需要用两个数组分别存储左边和右边的最高高度,空间复杂度较高。
方法二是用单调栈的方法解决这个问题。对于每一列,如果其高度小于栈顶元素,那么将其压入栈中,否则就弹出栈顶元素,并计算出其容纳的雨水量。容纳的雨水量可以通过计算当前列和栈顶元素之间的距离,以及它们之间的高度差来计算。这种方法比较巧妙,只需要一个栈就可以完成计算,空间复杂度较低。
需要注意的是,在方法二中,栈中存储的是每一列的下标,而不是高度。这是因为对于每一列,我们需要知道其左边和右边最高的列的高度,因此直接存储高度是不够的,需要存储下标。
这道题目可以帮助我们更好地理解单调栈的使用方法,以及如何通过单调栈来解决一些类似的问题。同时,这道题目也需要我们仔细思考每一个细节,例如如何计算容纳的雨水量,以及如何存储每一列的左边和右边最高的列的高度,这些都是需要仔细思考的。
总之,这道题目对于我们的编程能力和思维能力都是一个不错的挑战。通过不断地练习和思考,我们可以更好地掌握单调栈的使用方法,提高我们的编程能力和思维能力,同时也能够更好地应对类似的编程问题。
代码实现
package main
import "fmt"
func main() {
fmt.Println(method02([]int{5, 0, 2, 1, 4, 0, 1, 0, 3}))
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func method01(height []int) int {
l := len(height)
if l == 0 {
return 0
}
maxL := make([]int, l)
maxR := make([]int, l)
maxL[0] = 0
maxR[l-1] = 0
for i := 1; i < l; i++ {
if height[i-1] > maxL[i-1] {
maxL[i] = height[i-1]
} else {
maxL[i] = maxL[i-1]
}
if height[l-i] > maxR[l-i] {
maxR[l-1-i] = height[l-i]
} else {
maxR[l-1-i] = maxR[l-i]
}
}
sum := 0
var rain int
for i := 0; i < l; i++ {
rain = min(maxL[i], maxR[i]) - height[i]
if rain > 0 {
sum += rain
}
}
return sum
}
func method02(height []int) int {
var s []int
var ret int
for i, h := range height {
for len(s) > 0 && height[s[len(s)-1]] < h {
top := s[len(s)-1]
s = s[:len(s)-1]
if len(s) == 0 {
break
}
t := s[len(s)-1]
W := i - t - 1
H := min(height[t], h) - height[top]
ret += W * H
}
s = append(s, i)
}
return ret
}