一、题目描述:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水
三、题目分析:
大家都听说过木桶效应(讲一只水桶能装多少水取决于它最短的那块木板),所以一列能接多少水与两边比较短的列有关,也就是值比较小的列,当然值比本身列小的值不能积水,第一列和最后一列是无法积水,所以关系应该如下图:
树值为1的积水是与傍边的两列相关还是与其他列相关?根据图例关系 我们可以这么分析:
-
当1 左边仅有3时候,右边保持不变,1能接水值是多少取决于 3,因为3是左边最大的元素且小于右边最大的元素5
-
当1 增加4 时候,右边去除5,左边最大值是4,右边最大值是3.5 ,那么能接水的值是2.5
-
所以得出结论 某一列能接多少水:取决于左边最大值(leftMax)与右边最大值中(rightMax)的较小值即所min(leftMax,rightMax)- 列值。
1. 暴力求解
暴力求解过程为循环找到除了第一个和最后一个元素的leftMax与rightMax并累加计算
// 求解函数
func trap(height []int) int {
res,n := 0,len(height)
for i := 1; i < n-1; i++ {
// 定义 左最大 和右最大
leftMax, rightMax := 0, 0
// 循环求左最大
for j := i; j < n; j++ {
rightMax = getMax(height[j], rightMax)
}
// 循环求右最大
for j := i; j > 0; j-- {
leftMax = getMax(height[j], leftMax)
}
// 求解
res += getMin(leftMax, rightMax) - height[i]
}
return res
}
// 求最大值
func getMax(n1, n2 int) int {
if n1 > n2 {
return n1
} return n2
}
// 求最小值
func getMin(n1, n2 int) int {
if n1 > n2 {
return n2
}
return n1
}
在LeetCode 上消耗时间与内存分别是
当然上述代码肯定存在优化的空间,比如事先算好每一个列的左最大和右最大,存放到map 中,理论上的时间复杂度可以是是0(n),但我们都知道时间复杂度反应的是数据规模增长的变化。
- 双指针
先说一句双指针永远的神,我们合并每次都去求解左最大和右最大,先上代码
func trap2(height []int) int {
// 声明 左右指针
left, right := 0, len(height)-1
leftMax, rightMax, res := 0, 0, 0
for left <= right {
// 求左边最大和右边最大
leftMax = getMax(leftMax, height[left])
rightMax = getMax(rightMax, height[right])
if leftMax < rightMax {
res += leftMax - height[left]
left++
} else {
res += rightMax - height[right]
right--
}
}
return res
}
其实核心思路是一致的,leftMax和rightMax 表示的是height[0..left] 和``height[right..n-1]最高列的高度。
但以下代码求解的是真正的积水值吗?
if leftMax < rightMax {
res += leftMax - height[left]
left++
}
当 left =2 且right=4 情况如上图所示:
对于上图的情况,我们已经知道leftMax < rightMax了,至于这个rightMax是不是右边最大的,不重要。重要的是height[i]能够装的水只和较低的leftMax之差有关,也就是说我们只在乎min(leftMax,rightMax)。
四、说明 想起今年注册掘金和微信公众号后就遗忘了,所以发个文章。公众号是我个人的所以不算抄袭!