• 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 is able to trap after raining. 

    For example, 
    Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6.

     

    The above elevation map 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. Thanks Marcos for contributing this image!

    和前面一题Container with Most Water是非常类似的题目。但是这题并非找两个边界使其面积最大,而是要求所有雨水的面积,所以还是很大区别的。

    有多种解法,一种是基于栈的,另外一种两次扫描,获取每个柱子的左右两边最高的柱子,而每根柱子可以容纳的雨水就是min(max_left,max_right)。这是一次只算一根柱子的做法。栈的做法是每次横向算一个level(或多个level)的存储水量。

    两次扫描的代码很好理解,我的实现如下:

    class Solution(object):
        def trap(self, height):
            """
            :type height: List[int]
            :rtype: int
            """
            
            if not height or len(height) < 3:
                return 0
            n = len(height)
            left = [height[0]]
            right = [height[-1]]
            #一次完成左右最高值的扫描
            for i in xrange(1,n):
                if height[i-1] > left[-1]:
                    left.append(height[i-1])
                else:
                    left.append(left[-1])
                    
                if height[n-i] > right[-1]:
                    right.append(height[n-i])
                else:
                    right.append(right[-1])
            water = 0
    #计算面积
    for i in xrange(1,n-1): res = min(left[i],right[~i]) - height[i] if res > 0: water += res return water

    上述做法的时间复杂度为O(n),空间复杂度也为O(n),这种做法可以使用双指针使其空间复杂度降为O(1),是一个最优解,其做法是保存左边的一个最高值和右边的一个最高值,同时用left和right双指针来确定当前要处理的bar。当左边的最高值小于右边最高值时,且left指针所指向的bar低于左边最高值时,说明对于left的这个bar,我们已经找到了它左边和右边最高值的比较小的值:leftright,可以确定这个bar上可以灌的水。当右边的最高值高于左边的最高值时,同理可以操作。

    灌水类题的真谛是,bar左边的最高值,右边的最高值和bar本身奠定了这个bar的灌水基调。

    代码如下:

    class Solution:
        # @param heights: a list of integers
        # @return: a integer
        def trapRainWater(self, heights):
            #two pointer solution, also find every bar's left highest and right highest
            #not first so the area is the vertical area of each bar.
            if not heights or len(heights) < 3:
                return 0
            left = 0
            right = len(heights) - 1
            area = 0
            ans = [0]*len(heights)
            leftheight = heights[0]
            rightheight = heights[right]
            while left < right - 1:
                if leftheight < rightheight:
                    left += 1
                    print 'left:',left
                    if leftheight > heights[left]:
                        area += leftheight - heights[left]
                    else:
                        leftheight = heights[left]
                else:
                    right -= 1
                    if rightheight > heights[right]:
                        area += rightheight - heights[right]
                    else:
                        rightheight = heights[right]
            return area

    基于栈的做法,是使用栈计算一个递减序列,对于栈顶元素来说,栈内倒数第二个元素都比其高,一旦当前遍历到的元素比栈顶要高,则可以计算栈顶这个bar为bottom可以存储的横向水量。所有元素都会执行一次入栈。代码如下:

    if not height or len(height) < 3:
                return 0
            area = 0
            #decreasing stack , while  increasing also pop not calculate, because it was calculated by the next equal one
            #if the decresing stack still contain elements when meet end, leave them there , they cant't trap water
            stack = []
            for i in xrange(len(height)):
                while stack and height[i] >= height[stack[-1]]:
                    bottom = height[stack.pop()]
                    if stack:
                        bound = min(height[stack[-1]],height[i])
                        h = bound - bottom
                        w = i - stack[-1] -1
                        area += h*w
                stack.append(i)
            return area

    这题是使用单调递减栈来找到每个bar左边第一个比它高的bar,和右边第一个比它高的bar。每次要出栈一个元素时,就需要计算以当前bar高度为最低水位可以填充的水(注意这里是横向水量),最多可以填充的量是,(min(height[left_first], height[right_first]) - height[bar]) * (right_first - left_first)。 注意的情况是 一旦一个bar被填充了水之后,它的高度就变成了min(height[left_fist], height[right_first]). 左边或者右边还没处理的bar可以忽略这个bar的存在,继续添水。另外单调栈涉及到一个最后是否要清空栈的问题, 但是这题在最后,如果都是递减的情况,存不住水, 所以不需要压入-1来处理。

  • 相关阅读:
    最大最小值得判断代码
    等腰三角形的代码及各类代码
    Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别
    Java 集合类的特性
    Java 用程序给出随便大小的10 个数,序号为1-10,按从小到大顺序输出,并输出相应的序号?
    List、Map、Set三个接口,存取元素时,各有什么特点?
    Java 清除数组相同元素
    eclipse导入项目出现叹号处理方法:
    初学者-PHP笔记
    java 对象输入输出流
  • 原文地址:https://www.cnblogs.com/sherylwang/p/5540649.html
Copyright © 2020-2023  润新知