给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
如图:
这道题的解法依旧很简单,我们可以搜索两个相同数列,取他的指标相加减,也可以从底部向上依次累加,寻找每一层会存续的水,我们依旧先看暴力解法是否可以完成。
暴力解法
在暴力解法之中,我们可以一层一层的搜索,将每一层可能蓄积的水都做出来,直到最高的的水位,原理较为简单,具体注释在代码中表明,时间复杂度为 O(n2),我们可以稍微简化一下,让第二次遍历不需要遍历全部列表,具体代码如下:
func trap(arr []int) int {
output := 0
//设定开始和结束量柱子,就能将最高水位确定好,排除掉单个高柱的情况
start := 0
end := 0
//表明特殊值
if len(arr)==0 || len(arr) == 1{
return 0
}
for k :=1;k<=Maxheight(arr);k++{
start = Min(arr, k)
end = Max(arr, k)
for i:= start; i< end+1; i++{
if arr[i] < k {
output+=1
}
}
}
return unit
}
//找到最高的柱子
func Maxheight(arr []int) int{
max := arr[0]
for i := 1; i < len(arr); i++ {
if max< arr[i] {
max = arr[i]
}
}
return maxVal
}
//找到最开始的能承接水位的位置
func Min(arr []int,aa int) int {
index := 0
for i := 0; i <len(arr); i++ {
if arr[i] >= aa{
index = i
break
}
}
return index
}
//找到最后能承接水的位置
func Max(arr []int,aa int) int {
index := 0
for i := 0; i < len(arr); i++ {
if arr[i] <= aa{
index = i
break
}
}
return index
}
动态规划
我们看之前的暴力解法,是否可以简化,事实上,原来的暴力算法在每一次提升的时候,都要遍历一遍数组,找到左右两边的水位值,能否对此进行优化,就是下一步算法要做的事情,做动态规划的前提,就是先了解暴力算法如何解,如果能用动态规划将所有水位记录下来,然后将这个水位与当前位置进行比对,就能得出此处是否有积水,积水多少,遍历一次即可结束。
由于对于每处的最高水位要看左右两个最高的水柱,所以我们可以左右各经历一次遍历,取其水位较小值和当然位置进行对比,具体算法模型如图推导:
假设有如图的水柱:
我们先从左到右按照最高水位来计量:
然后按照从右到左的方向,以同样方式计量:
然后在遍历时取到他们的最小值,两相结合就可以得到他们的最高水位:
具体代码如下:
func trap(height []int)int{
if len(height)==0{
return 0
}
size :=len(height)
res :=0
leftDP :=make([]int,size)
rightDP :=make([]int,size)
leftDP[0]= height[0]
rightDP[size-1] = height[size-1]
for i:=1;i<size;i++{
leftDP[i] = max(leftDP[i-1],height[i])
}
for i:=size-2;i>=0;i--{
rightDP[i] = max(rightDP[i+1],height[i])
}
for i:=1;i<size-1;i++{
minHeight := min(leftDP[i],rightDP[i])
waterVal :=minHeight-height[i]
res +=waterVal
}
return res
}
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
}