Leetcode 042 接雨水 python解题报告(困难)

本人一直在努力地积累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端逼近可以轻松写出代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值