42. 接雨水

题目描述:

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

示例 1:

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

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

提示:

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

解题分析及思路:

对于下标 i,下雨后水能到达的最大高度等于下标 i 两边的最大高度的最小值,下标 i 处能接的雨水量等于下标 i 处的水能到达的最大高度减去 height[i]。

首先,我们可以使用动态规划的思想,通过一次正向遍历,预处理得到每个位置左侧和右侧的最大高度。

创建两个数组 maxL 和 maxR,其中 maxL[i] 表示下标 i 及其左边的位置中,height 的最大高度,maxR[i] 表示下标 i 及其右边的位置中,height 的最大高度。

最后,下标i处能够接到的水为minFunc(maxL[i], maxR[i]) - height[i],将所有的求和即为最终答案。

那么对应动态规划解法:

  1. 定义状态: 在这个问题中,我们可以定义状态为每个位置的左侧最大高度和右侧最大高度。我们使用两个数组 maxL 和 maxR 分别表示每个位置左侧和右侧的最大高度。

  2. 找到状态转移方程: 通过一次遍历,我们可以计算得到每个位置的左侧最大高度和右侧最大高度。状态转移方程为:

maxL[i] = max(height[i], maxL[i-1])
maxR[l-1-i] = max(height[l-1-i], maxR[l-i])
  1. 初始化: 初始化数组 maxL 和 maxR 中的第一个元素为对应位置的高度值。

  2. 递推求解: 通过一次遍历,更新每个位置的左侧最大高度和右侧最大高度。

  3. 计算最终结果: 通过再次遍历,计算每个位置上的雨水量,总和即为结果。

func trap(height []int) int {
	var maxFunc = func(i, j int) int {
		if i > j {
			return i
		}
		return j
	}
	var minFunc = func(i, j int) int {
		if i > j {
			return j
		}
		return i
	}
	var l = len(height)
	var maxL = make([]int, l)
	var maxR = make([]int, l)
	for i := 0; i < l; i++ {
		maxL[i] = height[i]
		maxR[l-1-i] = height[l-1-i]
		if i != 0 {
			maxL[i] = maxFunc(maxL[i], maxL[i-1])
			maxR[l-1-i] = maxFunc(maxR[l-1-i], maxR[l-i])
		}
	}
	var total int
	for i := 0; i < l; i++ {
		total += minFunc(maxL[i], maxR[i]) - height[i]
	}
	return total
}

整体的动态规划思路是通过两次遍历,分别计算每个位置的左侧和右侧最大高度,然后通过再次遍历计算每个位置上的雨水量,最终得到总的雨水量。

复杂度:

  • 时间复杂度:O(N),其中 n 为输入数组 height 的长度。
  • 空间复杂度:O(N),其中 n 为输入数组 height 的长度。

执行结果:

  • 执行耗时:8 ms,击败了89.64% 的Go用户
  • 内存消耗:5.9 MB,击败了24.68% 的Go用户

通过次数 1.1M 提交次数 1.7M 通过率 64.4%

Related Posts

1206. 设计跳表

## 题目描述:不使用任何库函数,设计一个 跳表 。跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。跳表相比于树堆与红黑树,其功能与性能相当,并且跳表的代码长度相较下更短,其设计思想与链表相似。例如,一个跳表包含 [30, 40, 50, 60, 70, 90] ,然后增加 80、45 到跳表中,以下图的方式操作。![](/img/leetcode/1

read more

41. 缺失的第一个正数

## 题目描述:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。示例 1:输入:nums = [1,2,0] 输出:3 解释:范围 [1,2] 中的数字都在数组中。示例 2:``` 输入:nums = [3,4,-1,1] 输出:2 解释

read more

736. Lisp语法解析

## 题目描述:给你一个类似 Lisp 语句的字符串表达式 expression,求出其计算结果。表达式语法如下所示: - 表达式可以为整数,let 表达式,add 表达式,mult 表达式,或赋值的变量。表达式的结果总是一个整数。 - (整数可以是正整数、负整数、0) - let 表达式采用 "(let v1 e1 v2 e2 ... vn en expr)" 的形式,其中 let 总

read more

899. 有序队列

## 题目描述:给定一个字符串 s 和一个整数 k 。你可以从 s 的前 k 个字母中选择一个,并把它加到字符串的末尾。返回 在应用上述步骤的任意数量的移动后,字典上最小的字符串 。**测试用例:**示例 1: ``` 输入:s = "cba", k = 1 输出:"acb" 解释: 在第一步中,我们将第一个字符(“c”)移动到最后,获得字符串 “bac”。 在第二步中,我们将第

read more

118. 杨辉三角

## 题目描述:给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。在「杨辉三角」中,每个数是它左上方和右上方的数的和。 示例 1: ``` 输入: numRows = 5 输出: [[1],[1,1],[1,

read more

120. 三角形最小路径和

## 题目描述:给定一个三角形 triangle ,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。示例 1: ``` 输入:triangle = [[2],[3

read more