接雨水问题独特解法

Leecode最热门的题之一

难度:困难

描述:给定 n 个非负整数表示每个宽度为 1 的柱子的高度,计算按此排列的柱子,下雨之后能接多少雨水。

示例一

输入:height=[0,1,0,2,1,0,1,3,2,1,2,1]    对应上面图中的黑色柱子分布

输出:6                                                   蓝色部分为上面柱子应接的的雨水量

示例二

输入:height=[4,2,0,3,2,5]

输出:9

关于这个问题,网上有各种解法,比如遍历法、双指针法,都显得很专业和高深,数据结构复杂,一般不太容易看懂,这里给出一个我自己的解法,是在没有看那些方法之前独立思考出来的,所以也没有受到那些方法的干扰。不知道网上有没有人用过这种或类似的方法?

分析(以示例一为例):

从左向右看,一个柱子右边可能接水,也可能不接水,这要看这个柱子右边有没有比它低的柱子,且低的柱子右边还有没有高一点的柱子,这要考查连续三个柱子的情况,貌似很复杂。

具体分析考虑:

第一个柱子高度为0,是不能接雨水的

第二个柱子高度为1,其右边第三柱子高度为0,接了1-0=1跟第二柱子等高的一段雨水(因为右边第四个柱子的高度为2)

第四个柱子高度为2,第五柱子高度为1,因此第四个柱子右边接雨水2-1=1,单从这里看是接不住雨水的,因为其右边的柱子高度还要低一些,还好第八根柱子的高度为3

第五柱子高度为1,第六柱子高度为0,按之前的思考,第五柱子右边应该接雨水为1-0=1,但实际接雨水量为2,所以这样思考好像也行不通,没有通用性,找不出规律

试了几种思路,感觉无从下手。

最后觉得下面的操作是可行的,设原始柱子高度列表为height

  1. 先找出柱子中除了高度为0的柱子之外的最低的柱子高度,设其高度为m,m=min(x for x in height if x>0)。
  2. 找出柱子中高度大于0的柱子范围,也即找到第一个高度不为0和最后一个高度不为0的柱子的索引号min_index和max_index,并计算出范围的长度l=max_index-min_index+1。
  3. 然后对height的元素进行如下处理:元素为0的不作处理保持为0,元素大于0的分别减去m,这样原height列表就会变成一个新的列表,其中柱子的高度统一减去了m。在这一个高度的柱子中接了多少雨水呢?可以很方便的计算出,应该是这一层的面积l*m减去原来是柱子的面积,剩下就是雨水的面积。
  4. 把新生成的height列表又进行这样的处理,然后把每一层接的雨水加起来,最后就可以得到总的雨水量。

程序如下:

#函数maxindex是找出nums列表中数字大于0的元素中索引号最大的索引号

def maxindex(nums):

    n=len(nums)

    i=1

    while i<=n:

        if nums[-i]>0:

            return n-i

        else:

            i=i+1

    else:

        return -1



#函数minindex是找出nums列表中数字大于0的元素中索引号最小的索引号

def minindex(nums):

    for i in range(len(nums)):

        if nums[i]>0:

            return i

    else:

        return -1

#对height列表进行处理,返回每个元素减去最小项元素值的新列表和存储的雨水   

def chuli(height):

    new_height=[]

    max_index=maxindex(height)

    min_index=minindex(height)

    m=min(x for x in height if x>0)

    s=0

    l=max_index-min_index+1

    for i in height:

        if i==0:

            new_height.append(0)

        else:

            new_height.append(i-m)

            s=s+m            

    return (new_height,l*m-s)

#检查列表元素是否都为0

def checklist(nums):

    for i in nums:

        if i==0:

            continue

        else:

            return False

    else:

        return True

height=eval(input('pls input height='))

s=0

while not checklist(height):

    x,y=chuli(height)

    print(x,y)

    height=x

    s=s+y

print(s)

运行实例一结果:

pls input height=[0,1,0,2,1,0,1,3,2,1,2,1]

[0, 0, 0, 1, 0, 0, 0, 2, 1, 0, 1, 0] , 2

[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0] , 4

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] , 0

6

运行实例二结果:

pls input height=[4,2,0,3,2,5]

[2, 0, 0, 1, 0, 3] , 2

[1, 0, 0, 0, 0, 2] , 3

[0, 0, 0, 0, 0, 1] , 4

[0, 0, 0, 0, 0, 0] , 0

9

总结:本解法采用对柱子按高度进行分层处理的方法,巧妙地设计出层处理模式,即以最低的柱子高度作高,以柱子所在的范围作宽度,形成一个长方形作为一层,用层面积(长方形面积)减去柱子占用面积,即可计算出每层接的雨水量,然后循环处理每层,完成总雨水量的计算,方法简便、容易理解,不涉及复杂的算法知识概念。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值