本人一直在努力地积累Leetcode上用Python实现的题,并且会尽力讲清每道题的原理,绝不像其他某些博客简略地带过。
如果觉得讲的清楚,欢迎关注。
如果觉得讲的清楚,欢迎关注。
题目:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
这道题是自己写的,虽然AC了但速度只有百分之2.。。觉得丢人于是立下flag再好好研究这道题。
题目加了个困难mark一下,以后经常复习一下。
为什么我的速度这么慢呢?可能是我用的是dp的思路。代码也会po,但大家笑笑就好。
看看网上流行的解法:
思路一:
2端逼近法:
先找到最高木板的index, 然后从2边用指针一直向这块木板逼近,同时更新最大面积。
这道题赖皮的地方在于找到了最大木板的index,于是在我们向这块最大木板逼近时,我们不用担心右边界或者左边界过低的问题。只用记录移动中的高峰,然后不断更新我们的面积即可。
class Solution:
def trap(self, listp):
"""
:type height: List[int]
:rtype: int
"""
#记录指针移动过程中的高峰
movepeak = 0
#记录最大面积
triprain = 0
#记录整体最大木板的index
maxindex = 0
#先遍历数组找到最大那块板的index
for i in range(1, len(listp)):
if listp[i] > listp[maxindex]:
maxindex = i
#再从前往后向maxindex那里遍历
#这里不用担心右边的木板最终会比我们的Movepeak小,因为我们有maxindex挡着
for i in range(0, maxindex):
if movepeak < listp[i]:
#遇到新的高峰,就更新我们的移动最高峰,标志着上一段结束了,接下来应该用新高峰去计算
movepeak = listp[i]
else:
#因为每次前进一步,所以面积的宽度始终为1,也就不用写了
triprain += movepeak - listp[i]
#重置高峰
movepeak = 0
#从右向左遍历
for j in range(len(listp) - 1, maxindex, -1):
#类似的思路
#因为有maxindex的存在,使得我们的面积可以不断肆无忌惮的更新
if movepeak < listp[j]:
movepeak = listp[j]
else:
triprain += movepeak - listp[j]
return triprain
我的丑陋写法:
总体思路:遍历每一块板,找以这块板为左边那条边的桶子的面积。另一条边怎么找呢,用p指针去搜索。
最后计算面积的方式比较神奇,需要各位自行体会一下了,总体概括一下就是分不同部分切割来求,比较复杂。。
class Solution:
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
#储存最终的最大面积
res = 0
#如果只有2块板,无法构成接水器
if len(height) <= 2:
return 0
#遍历所有木板(除了最后一个,因为无法以最后一块木板为头木板)
for i in range(0, len(height)-1):
#如果以当前木板大于后面的一块板,这才有可能以i开始的构成的桶子能接水
if height[i] > height[i+1]:
#设置一些index指针
k = i+1
p = k
#当p和k位的元素都小于以i开始的板时,我们的p不断前进变大。
while height[p] < height[i] and height[k] < height[i]:
p += 1
#如果p不小心越界了,说明我们当前的i上的高度比较大
if p >= len(height):
#如果我们i板的高度比height的高度大太多了,超过了1
if height[i] > height[i+1] + 1:
#我们令i的高度减少一点,再去找能不能构成桶的另一条边
height[i] = height[i] - 1
#指针重新归位
p = k
#继续搜索
continue
else:
#如果height[i] 与下一项板子长度差为1,我们就break,直接进到下一个板子。
break
#如果说遇到一条板子(p),它的高度比i大了,我们就构成了一个桶,于是更新面积
if height[p] >= height[i]:
#先找这之中最高的板
partsum = max(height[i+1:p])
#差距为i板的高度减去第二高的板的高度
dif = height[i] - partsum
#计算面积
onesum = (p-i-1)*(dif)
#加上这一块的面积
res += onesum
#我们的height[i]也得减少一点
height[i] -= dif
#根据大小关系重新定义指针的位置
if height[i] > height[k]:
p = k
else:
p = k + 1
return res
总结:找这种柱形图接水的面积,好的思路会事半功倍,就如上面的对比。总的来说最高板子是这个数组的轴,利用好这根轴,我们从2端逼近可以轻松写出代码。