leetcode刷题笔记四十二 接雨水

leetcode刷题笔记四十二 接雨水

源地址:42. 接雨水

问题描述:

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

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

leetcode刷题笔记四十二  接雨水

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

代码补充:

//方法一: 直接统计每个柱子的储水情况
//对于单个柱子的储水值,受其左侧柱子最大高度及右侧柱子最大高度
//即 储水值 = (左侧最大柱子高度与右侧最大柱子高度的较小值)- 当前柱子高度
//实际处理过程中,需要将当前柱子加入到左右侧柱子最大高度的计算中,防止出现负数值,这种情况下整个数组的最大高度的储水值会为0
//时间复杂度O(n^2) 空间复杂度O(1)

import scala.math
object Solution {
    def trap(height: Array[Int]): Int = {
        var res = 0
		
        //就整个数组中的每个柱子的储水值进行计算
        for (i <- 0 until height.length){

            //将当前柱子高度包含在内,计算左侧柱子最大高度
            var max_left = 0
            for(j <- 0 to i){
                if (height(j) > max_left) max_left = height(j)
            }

			//将当前柱子高度包含在内,计算右侧柱子最大高度
            var max_right = 0
            for(j <- i until height.length){
                if (height(j) > max_right) max_right = height(j)
            }

            //将每个柱子上的储水值进行统计
            res += (math.min(max_left, max_right) - height(i))    
        }
        return res
    }
}


//方法二: 动态规划
//基于上述方法,可见对于每个柱子使用的数据主要为其max_left及max_right
//构建两个数组 记录每个柱子的max_left和max_rigth
//时间复杂度O(n) 空间复杂度O(n)
import scala.math
object Solution {
    def trap(height: Array[Int]): Int = {
        if (height.length == 0) return 0 
        var res = 0
        val max_left_matrix = new Array[Int](height.length)
        val max_right_matrix = new Array[Int](height.length)
        //初始化数组
        max_left_matrix(0) = height(0)
        max_right_matrix(height.length-1) = height(height.length-1)
		
        //更新左侧柱子最大高度数组
        for(i <- 1 to height.length-1) max_left_matrix(i) = math.max(max_left_matrix(i-1),height(i))

		//更新右侧柱子最大高度数组
        //注意Scala倒叙计数的方法, 使用reverse方法
        for(i <- (0 to height.length-2).reverse) max_right_matrix(i) = math.max(max_right_matrix(i+1), height(i))

        //根据公式,计算每个柱子的储水值并累加
        for(i <- 0 to height.length-1) res += math.min(max_left_matrix(i), max_right_matrix(i)) - height(i)

        return res
    }
}

//方法三:双指针法
//维持一个稳定的max_left 和 max_right,将随着left,right指针收敛进行更新
//时间复杂度O(n) 空间复杂度O(1)
import scala.math
object Solution {
    def trap(height: Array[Int]): Int = {
        if (height.length == 0) return 0 

        var res = 0
        var max_left = 0
        var max_right = 0
        var left = 0
        var right = height.length-1

        
        while(left <= right){
            //max_left <= max_right时,使用max_left进行判断
            //推动left向前,因为max_left较max_right小,若left
            //不向前,始终会选择max_eft作为评价
            if(max_left <= max_right){
                max_left = math.max(max_left, height(left))
                res += max_left - height(left)
                left += 1
            }
            //同left直至双指针重合
            else{
                max_right = math.max(max_right, height(right))
                res += max_right - height(right)
                right -= 1
            }
        }

        return res
    }
}

//方法四:单调栈
//储水的槽构成需要三根柱子,即单调栈中比栈顶元素大的左侧柱
//栈顶元素 与 目前访问的数组的柱高 这才可能构成凹槽储水
//以图例演示储水情况
// 4
// 4 3 
// 4 3 2
// 4 3 2 0  ===》 因为缺乏有效的右侧,无法计算储水
// 4 3 2 0  <--- 1 此处构成凹槽  将0弹出 计算高度为0的层的储水值 res += (4-2-1)*(1-0) 
// 4 3 2 1 <---3 此处又构成凹槽  将1弹出 计算高度为1的层的储水值 res += (5-2-1)*(2-1)
//以此类推
import scala.math
import scala.collection.mutable.Stack
object Solution {
    def trap(height: Array[Int]): Int = {
        if (height.length == 0) return 0 
        
        var res = 0
        val stack = new Stack[Int]()
        
        for(i <- 0 to height.length-1){
            var last = 0
            while(stack.isEmpty == false && height(stack.top) <= height(i)){
                val temp = stack.top
                stack.pop
                res += (i - temp - 1)*(height(temp) - last)
                last =   height(temp)
            }
            if (stack.isEmpty == false) res += (i - stack.top - 1)*(height(i) - last)
            stack.push(i)
        }

        return res
    }
}