当青训营遇上码上掘金会发生什么奇妙的事情呢,那就不得不提到这个可爱又经典的题目啦。
题目描述
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少单位的青豆。
不考虑边角堆积,柱子和青豆在二维切面中的每格视为 1 单位。
示例:
输入:heights = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
分析
首先分析能够存青豆的前提条件,显然需要当前位置的左右两边均要有高于当前位置高度的柱子,且青豆的数量取决于最矮的一端。
因此最先应该可以想到使用双指针的方法,从整个 n 个柱子的左右两端开始逐渐向中间移动,计算移动过程中的青豆数。同时使用两个变量 maxLeftHeight 和 maxRightHeight 分别记录移动期间左右两端柱子的最大高度,随着指针的移动不断进行更新。
接下来就需要思考如何计算每个位置的青豆数。
刚才提到,每个位置的青豆数取决于当前位置左右两端最矮一端的柱子高度,而高柱子并没有起到对于青豆数计算的决定性作用,因此每次都将矮柱子一端的指针向中间移动,计算每个位置的青豆数量,直至二指针相遇。
最后就是每个位置青豆数的计算的问题。
这个就显而易见了,只需要计算矮柱子和当前位置的高度差就可以啦。
代码
关键代码及注释
func trapBean(heights []int) (ans int) {
// 高度数组不符合计算要求,直接返回
if heights == nil || len(heights) <= 2 {
return 0
}
// 左右两端指针
left, right := 0, len(heights)-1
// 左右两端移动过程中最大高度
maxLeftHeight, maxRightHeight := heights[left], heights[right]
// 两指针相遇时结束
for left < right {
// 获取最大高度
maxLeftHeight = max(maxLeftHeight, heights[left])
maxRightHeight = max(maxRightHeight, heights[right])
// 判断哪端矮,就用哪端的最大高度减去当前高度得到当前位置的青豆数
if heights[left] <= heights[right] {
ans += maxLeftHeight - heights[left]
left++
} else {
ans += maxRightHeight - heights[right]
right--
}
}
return
}
代码片段
复杂度分析
- 时间复杂度: 每次移动只进行减法计算,且两指针总共移动次数不会超过高度数组长度 n,所以时间复杂度为
O(n) - 空间复杂度: 只使用了变量进行存储,所以空间复杂度为
O(1)