LeetCode42. Trapping Rain Water

42. Trapping Rain Water

接雨水问题
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.
给定n个非负整数,每个整数代表高度图里面每个宽度为一的柱子。计算当下雨的时候,柱状中所表达的图形能接到多少雨水。

Example 1:

图例

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Explanation: The above elevation map (black section) is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.

Example 2:
Input: height = [4,2,0,3,2,5]
Output: 9

Constraints:

  • n == height.length
  • 1 <= n <= 2 * 10<sup>4</sup>
  • 0 <= height[i] <= 10<sup>5</sup>

题目分析

之前用动态规划找到过一道计算给定的木板长度围成的容器最大容积的题目,和这个类似(11.Container With Most Water)。当时的动态规划思路是,左右双指针,哪边的短就移动哪边的下标,向内收敛。并每次计算并比较找出最大容积。
我们尝试从这里找到类似的解决方案。我们遍历数组,找到比大于或者等于他的第一个长度的数组下标,计算其中容积。
以此类推。

class Solution:
    def cal_contain(self, height):
        h = height[0]
        ret = 0
        for i in range(1, len(height)):
            if h>height[i]:
                ret += h-height[i]
        return ret
            
    def trap(self, height: List[int]) -> int:
        left = 0
        right = 1
        l_h = len(height)
        ret = 0
        while left < l_h and right < l_h:
            while right < l_h and height[right]< height[left]:
                right += 1
            if right < l_h:
                ret += self.cal_contain(height[left:right])
                left = right
                right += 1
            
        left = 0
        right = 1
        height.reverse()
        while left < l_h and right < l_h:
            while right < l_h and height[right]<= height[left]:#注意这里用了小于等于。为了排除重复
                right += 1
            if right < l_h:
                ret += self.cal_contain(height[left:right])
                left = right
                right += 1

        return ret

两次循环。第一次从左边找,找左边是最高边,然后围成的容积。第二遍将列表转置,然后重复第一次的操作

答案解析

暴力法

算法:
从左至右遍历字符串。
然后每个节点分别向右和向左迭代。并计算可能的容积
将所有数值相加。
时间复杂度O(n^2)
空间复杂度O(1)

动态规划

class Solution:
    def trap(self, height: List[int]) -> int:
        if not height:
            return 0
        
        n = len(height)
        leftMax = [height[0]] + [0] * (n - 1)
        for i in range(1, n):
            leftMax[i] = max(leftMax[i - 1], height[i])

        rightMax = [0] * (n - 1) + [height[n - 1]]
        for i in range(n - 2, -1, -1):
            rightMax[i] = max(rightMax[i + 1], height[i])

        ans = sum(min(leftMax[i], rightMax[i]) - height[i] for i in range(n))
        return ans

先分别用一次循环找出每个节点左边的最大值,和右边的最大值。
容积取决于两边最大值中的最小值。
时间复杂度O(n)
空间复杂度O(n)

单调栈

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        stack = list()
        n = len(height)
        
        for i, h in enumerate(height):
            while stack and h > height[stack[-1]]:
                top = stack.pop()
                if not stack:
                    break
                left = stack[-1]
                currWidth = i - left - 1
                currHeight = min(height[left], height[i]) - height[top]
                ans += currWidth * currHeight
            stack.append(i)
        
        return ans

双指针

降低空间复杂度的动态规划。利用双指针代替在动态规划中创建的左右列表

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        left, right = 0, len(height) - 1
        leftMax = rightMax = 0

        while left < right:
            leftMax = max(leftMax, height[left])
            rightMax = max(rightMax, height[right])
            if height[left] < height[right]:
                ans += leftMax - height[left]
                left += 1
            else:
                ans += rightMax - height[right]
                right -= 1
        
        return ans

总结

我的方法感觉也未尝不可,属于双指针和动态规划的中间版本。还有待更精进。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容