双指针解决leetcode 42. 接雨水 与 11. 盛最多水的容器
第一次在leetcode A一道high难度的题,纪念一下
这道题思路很简单,设想一下,盛多少水是由最低的墙决定的,我们设两个指针,左指针l从头开始,右指针r从尾部开始,那么当前的容量v = min(height[l] ,height[r]) * len(l , r)。
那么下次用来和这次比较大小的容积是怎么找的呢?
很简单,设想一下,左右向内移动,那么len一定会减少,那么我们一定想让min尽可能大,所以只要让l和r中高度最小的变化就可以了。
浅证一下:如果当前height[l] < height[r] ,那么 v = min(height[l] ,height[r]) * len(l , r) = height[l] * len(l , r) , 又已知变化后的lenNew < 变化前的len ,那么向左移动右指针设此时 r 变为 rNew
如果 height[rNew] >= height[r] 那么 min = height[l] , vNew = height[l] * lenNew < v
如果 height[rNew] < height[r] 那么 min <= height[1] , vNew <= height[l] * lenNew < v
由以上可知,如果移动高度小的指针得到的容积必然比原来的小,所以上面要求动高度最小的
所以代码轻轻松松出来了:
func maxArea(height []int) int {
len := len(height)
l := 0
r := len - 1
ans := 0
for {
ans = max(ans , sumAns(height , l , r))
if height[l] < height[r] && l != r {
l++
}else{
r--
}
if l == r {
return ans
}
}
return ans
}
func sumAns(h []int,l int,r int) int{
return min(h[l] , h[r]) * (r - l)
}
func max(a int,b int) int {
if a > b {
return a
} else {
return b
}
}
func min(a int,b int) int {
if a < b{
return a
}else {
return b
}
}
这时候,我看到了另一个题,
< 42. 接雨水
看到题目的描述,也是容积的问题,我就想能不能通过双指针解决。
一开始,我的思路是通过快慢指针,找到右边快指针所在墙壁小于慢指针所在墙壁的地方,然后差值加入答案,后来发现,全是error,ToT
仔细一想,其实当前位置所能积水的量,实际上由当前位置左边最大墙和右边最大墙中最小的墙决定的,是不是很拗口qwq,上图
解决了思路,那么我们就该考虑代码了
还是快慢指针,第一次可以判断,左边最大的墙leftmax 就是慢指针当前的墙高,右边最大的rightmax 就是快指针的墙高,那么第一次不管是快指针所在的容积和慢指针所在的容积都是0,
那么下一个位置该判断谁的容积呢?或者说是快指针先动还是慢指针先动呢?
其实这里和上面那道题一样,小的先动,为什么呢?
如果当前左边墙已经比右边墙高了,由上面可知,能不能积水看的是最低的墙,那么左边的位置一定是不能装水的,需要右边位置向里面挪,找到下一个需要积水的位置。
那么找到了需要积水的位置如何计算?向那个方向移动的指针,结果就等于这个方向上的最大墙-当前的墙高,为什么呢?
因为我们每次都是小的先动,例如右边小,右边向里动,那么左边的一定是高的,那么就把右边小的里面最大的找出来减掉当前的墙高就是结果
那么我们上代码:
func trap(height []int) int {
l := 0
r := len(height) - 1
maxl := height[l]
maxr := height[r]
ans := 0
if len(height) <= 1 {
return 0
}
for {
maxl = max(maxl,height[l])
maxr = max(maxr,height[r])
if maxl >= height[r] {
ans += maxr - height[r]
r--
}else if maxr >= height[l] {
ans += maxl - height[l]
l++
}
if r == l {
break
}
}
return ans
}
func max(a int,b int) int{
if a > b {
return a
}else {
return b
}
}