【leetcode系列】【算法】【困难】接雨水

题目:

题目链接: https://leetcode-cn.com/problems/trapping-rain-water/

解题思路:

方法一:动态编程

每个位置可以保存的水量,是当前位置左右两边最大高度的较小值,减去当前高度

所以只要知道每个位置的左右最大高度,就可以计算出当前位置的储水量

但是如果暴力每次都遍历当前位置的左右最大高度,效率过低

所以可以在遍历前,先预处理每个位置的左右最大高度,再遍历的时候,就不用每次都向左右遍历完所有数据了

在预处理每个位置的左右最大高度时,使用dp的思想:

  1. index = 0时,左边的最大高度是自己
  2. index > 0时,左边的最大高度是max(index - 1, index)
  3. 右边最大高度同理,只是修改为从后向前遍历

具体图示如下:

每个位置右边的最大高度如下图所示(黄色格子部分为每个柱子的高度,蓝色部分为最高的右边柱子值):

每个位置左边的最大高度如下图:

两幅图的重叠区域为:

此部分即为面积

方法二:栈

在遍历时维护一个栈,如果当前位置的柱子高度小于栈顶柱子高度,则将当前柱子高度入栈;如果大于栈顶柱子高度,则将栈中的元素依次出栈计算储水量,图示如下:

遍历开始,第一个元素直接入栈(栈中保存的是数组下标,用于计算长度)

第二个元素入栈前,发现比前一个柱子高度高,此时先将上一个柱子出栈,判断上一个柱子左右两个柱子的高度

此时发现没有左边的柱子,所以不进行面积的计算,直接将1入栈,继续下次循环

继续遍历到下标为3的位置,发现3比2的柱子高度要高

此时,在3入栈前,对3之前的有效面积进行计算,计算逻辑为:每个柱子的左右最低高度,减去当前柱子的高度,乘以长度

对于下标为2的位置来说,具体计算式子如下:

  1. 左右最低高度min_height = min(height[3], height[1]) = min(2, 1) = 1
  2. 当前高度current_height = height[2] = 0
  3. 长度length = 3 - 1 - 1 = 1
  4. 有效面积area = (min_height - current_height) * length = (1 - 0) * 1 = 1

继续对1进行判断时,发现栈中1左边没有柱子了,此时停止计算有效面积,将3入栈,继续遍历:

遍历到下标为6时,比栈顶元素5的柱子高度高,继续按照上述逻辑计算面积后,增加面积为1,之后栈的情况如下:

此时栈顶的下标为4,但是6的高度与4的高度相同,停止对栈进行遍历,将6入栈后,继续遍历:

遍历到7时,7的高度比6的高,再次开始对栈的遍历,首先对6在栈中左边的柱子高度和当前位置7的进行比对,逻辑如下:

  1. 左右最低高度min_height = min(height[4], height[7]) = min(1, 3) = 1
  2. 当前高度current_height = height(6) = 1
  3. 长度length = 7 - 4 - 1 = 2
  4. 有效面积area = (min_height - current_height) * length = (1 - 1) * 0 = 0

此时将6出栈后,继续对栈顶元素判断,发现7的高度比4的高度高,继续对栈进行遍历,此时栈的情况如下:

此时对4位置的计算逻辑如下:

  1. 左右最低高度min_height = min(height[3], height[7]) = min(2, 3) = 2
  2. 当前高度current_height = height(4) = 1
  3. 长度length = 7 - 3 - 1 = 3
  4. 有效面积area = (min_height - current_height) * length = (2 - 1) * 3 = 3

此时栈情况如下:

按照此逻辑继续遍历,直到遍历完所有柱子

方法三:双指针

left头指针,right尾指针双指针遍历,当前位置的面积,有下述几种情况:

  1. 如果height[left] > height[right],当前位置的面积由height[right]决定
  2. 其他情况,由height[left]决定

具体提示流程图如下:

变量如下:

  1. left_max: 当前遍历的左边最大高度,初始化为0
  2. right_max: 当前遍历的右边最大高度,初始化为0
  3. left: 左指针,初始化为0
  4. right: 右指针,初始化为len(height) - 1,即最后一个元素下标
  5. area: 累加面积,初始化为0

初始位置如下:

此时发现height[right] > height[left],计算left处的面积,逻辑如下:

  1. 比对height[left]和left_max大小,发现此时height[left] = 0, left_max = 0,无须计算面积
  2. 向右移动left = left + 1

处理完后的指针情况如下:

left和right相等,此时处理逻辑如下:

  1. 比对height[left]和left_max大小,发现height[left] > left_max,更新left_max = height[left],不计算面积
  2. 向右移动left(或者向左移动right,道理相同)

处理完后的情况如下:

此时处理逻辑如下:

  1. height[left] < left_max,计算left处的面积 = left_max - height[left] = 1 - 0 = 1
  2. 继续向右移动left

处理完成结果及后续图示如下:

代码实现:

方法一:

class Solution:
    def trap(self, height: List[int]) -> int:
        if 0 == len(height):
            return 0
        
        left_max, right_max = [0] * len(height), [0] * len(height)
        left_max[0] = height[0]
        right_max[-1] = height[-1]
        
        for index in range(1, len(height)):
            left_max[index] = max(height[index], left_max[index - 1])
            
        for index in range(len(height) - 2, -1, -1):
            right_max[index] = max(height[index], right_max[index + 1])
            
        area = 0
        for index in range(len(height)):
            area += min(left_max[index], right_max[index]) - height[index]
            
        return area

方法二:

class Solution:
    def trap(self, height: List[int]) -> int:
        area = 0
        rec = []
        for index in range(len(height)):
            while 0 < len(rec) and height[index] > height[rec[-1]]:
                last_idx = rec[-1]
                rec.pop()
                if 0 == len(rec):
                    break
                    
                distance = index - rec[-1] - 1
                tmp_height = min(height[index], height[rec[-1]]) - height[last_idx]
                area += distance * tmp_height
                
            rec.append(index)
            
        return area
            

方法三:

class Solution:
    def trap(self, height: List[int]) -> int:
        left_max, right_max = 0, 0
        left, right = 0, len(height) - 1
        area = 0
        while left < right:
            if height[left] > height[right]:
                if height[right] >= right_max:
                    right_max = height[right]
                else:
                    area += right_max - height[right]
                    
                right -= 1
            else:
                if height[left] >= left_max:
                    left_max = height[left]
                else:
                    area += left_max - height[left]
                    
                left += 1
        
        return area
发布了100 篇原创文章 · 获赞 4 · 访问量 1470

猜你喜欢

转载自blog.csdn.net/songyuwen0808/article/details/105312639