leetcode------接雨水(动态规划,双指针)

解法一:按行算:

整个思路就是,求第 i 层的水,遍历每个位置,如果当前的高度小于 i,并且两边有高度大于等于 i 的,说明这个地方一定有水,水就可以加 1。

如果求高度为 i 的水,首先用一个变量 temp 保存当前累积的水,初始化为 00。从左到右遍历墙的高度,遇到高度大于等于 i 的时候,开始更新 temp。更新原则是遇到高度小于 i 的就把 temp 加 1,遇到高度大于等于 i 的,就把 temp 加到最终的答案 ans 里,并且 temp 置零,然后继续循环。

我们就以题目的例子讲一下。

先求第一行的水

再求第 2 行的水。

如此类推。

代码:

def solution(l):
    max_height = max(l)
    sum = 0
    for y in range(1,max_height+1):
        update_flag = False
        temp = 0
        for x in range(len(l)):
            if l[x] >= y:
                update_flag = True
                sum += temp
                temp = 0
            if l[x]<y and update_flag ==True:
                temp+=1
    return sum
print(solution([0,1,0,2,1,0,1,3,2,1,2,1])) #6

时间复杂度:如果最大的数是 m,个数是 n,那么就是 O(m*n)。

空间复杂度:O(1)。

 

解法二:按列算:

求每一列的水,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。

装水的多少,当然根据木桶效应,我们只需要看左边最高的墙和右边最高的墙中较矮的一个就够了。

所以,根据较矮的那个墙和当前列的墙的高度可以分为三种情况。

  • 较矮的墙的高度大于当前列的墙的高度
  • 把正在求的列左边最高的墙和右边最高的墙确定后,然后为了方便理解,我们把无关的墙去掉。
  • 这样就很清楚了,现在想象一下,往两边最高的墙之间注水。正在求的列会有多少水?

    很明显,较矮的一边,也就是左边的墙的高度,减去当前列的高度就可以了,也就是 2 - 1 = 1,可以存一个单位的水。

  • 较矮的墙的高度小于当前列的墙的高度
  • 同样的,我们把其他无关的列去掉。
  • 想象下,往两边最高的墙之间注水。正在求的列会有多少水?

    正在求的列不会有水,因为它大于了两边较矮的墙。

  • 较矮的墙的高度等于当前列的墙的高度。

    和上一种情况是一样的,不会有水。

  • 代码:

def solution(l):
    flag = False
    sum = 0
    for i in range(1,len(l)):  #因为第一列肯定是没雨水的,所以从第二列开始
        if l[i] == 0 and flag == False:
            continue
        else:
            left_max = 0
            right_max = 0
            temp = 0
            flag = True
            for left in range(i):
                left_max = max(left_max,l[left])
            for right in range(i+1,len(l)):
                right_max = max((right_max,l[right]))
            shorter = min(left_max,right_max)
            if shorter != l[i] and shorter>l[i]:
                temp = shorter - l[i]
                print('i={},shorter={},temp={},leftmax={},rightmax={}'.format(i,shorter,temp,left_max, right_max))
                sum += temp
    return sum
print(solution([0,1,0,2,1,0,1,3,2,1,2,1])) #6

时间复杂度:O(n²),遍历每一列需要 nn,找出左边最高和右边最高的墙加起来刚好又是一个 n,所以是 n²。

空间复杂度:O(1)。

 

解法三:动态规划:

动态规划是基于解法二上的,即是优化解法二的。

在解法二中,每一列都需要找出其左边d最高的柱子和右边最高的柱子,在解法二中每一个都去遍历寻找左,右最高的柱子会出现计算重复,因此用动态规划来找每一列的左,右最高的柱子。

left_max[i]=max(left_max[i-1],l[i-1]]
right_max[i]=max(right_max[i+1],l[i+1]]

上面的两条式子,就可以看出动态规划的影子,即当前状态可以由之前状态推导而出。

代码:

def solution(l):
    left_max_list=[0 for _ in range(len(l))]
    right_max_list = [0 for _ in range(len(l))]
    sum = 0
    for i in range(1,len(l)):  #求出各个列表中左边最大的柱子,从1开始是因为第一个柱子肯定是没有雨水的
        left_max_list[i] = max(left_max_list[i-1],l[i-1])
    for i in range(len(l)-2,-1,-1):  #求出各个列表中右边最大的柱子,从-2是因为最后一个柱子肯定也是没有雨水的
        right_max_list[i] = max(right_max_list[i+1],l[i+1])
    for i in range(len(l)):
        shorter = min(left_max_list[i],right_max_list[i])
        if shorter > l[i]:
            sum += shorter - l[i]
    return sum
print(solution([0,1,0,2,1,0,1,3,2,1,2,1])) #6

时间复杂度:O(n)。

空间复杂度:O(n),用来保存每一列左边最高的墙和右边最高的墙。

解法四:双指针

双指针是在数组的头和尾都插入一个指针。

然后两个指针会往中间移动,从而计算每列的水量。

在这道题的思路如下:

基于木桶原理,装的水量 取决于最短的一个柱子。

每次决定是左指针 left_p移动还是右指针 right_p移动时,先判断 left_p 前一个柱子高还是 right_p后一个柱子高,由于水量是取决于较低的柱子的,所以若是 left_p 后一个的柱子较低,则这轮先计算left_p柱子上的水,然后移动 left_p。反之亦然。

无论是 left_p 还是 right_p,没经过一个柱子都会记录下,经过的这些柱子中,最高的柱子的高度是多少。因为这并不是单纯的木桶问题,知道是移动 left_p 还是 right_p后,left_p或right_p位置最高能装多少水是取决于 left_p经过的最高柱子的,如下图:

若当前的left_p 和 right_p如图所示。right_p 的前一个柱子比 left_p的前一个柱子高,因此计算left_p的水量。如图很明显,left_p的最大水量应该和left_max的高度是相等的。同理,对于right_p也是一个道理。所以left_p和 right_p都必须记录一个 left_max和 right_max来保存各自方向的最高柱子的高度。

def solution(l):
    sum = 0
    left_max = 0
    right_max = 0
    left_p = 1
    right_p = len(l)-2
    n = len(l)
    for i in range(n-1):
        if l[left_p-1] < l[right_p+1]:
            left_max = max(left_max,l[left_p-1]) #left_max是记录当前left位置之前的最高柱子
            min = left_max
            if min>l[left_p]: sum += (min - l[left_p])
            left_p+=1
        else:
            #print(i)
            right_max = max([right_max,l[right_p+1]])  ##right_max是记录当前right位置之前的最高柱子
            min = right_max
            if min > l[right_p]: sum += (min - l[right_p])
            right_p -= 1
    return sum
print(solution([0,1,0,2,1,0,1,3,2,1,2,1])) #6

时间复杂度:O(n),只需要一次循环就能完成。

空间复杂度:O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值