LeetCode-42-接雨水
题目
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
1 |
|
示例 2:
1 |
|
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
题解
法一:动态规划
本题求解的思路为:对于i
处,下雨后水能达到的最大高度为i
位置左右两边最大高度的最小值,然后i
处能接到的雨水量为i
处水能达到的最大高度减去height[i]
。
这里我们使用两个数组leftMax
、rightMax
,leftMax
表示i
处左边的最大高度,rightMax
表示i
处右边的最大高度;
- 首先
leftMax
与rightMax
初始化,leftMax[0] = height[0]
,rightMax[n - 1] = height[n - 1]
- 对于
leftMax
,我们从1
开始遍历height
数组,leftMax[i] = max(leftMax[i - 1], height[i])
- 对于
rightMax
,从n - 2
处开始倒着遍历height
数组,rightMax[i] = max(rightMax[i + 1], height[i])
- 最后我们取
ans = min(leftMax[i], rightMax[i]) - height[i]
,再累加求和即可
1 |
|
方法二:单调栈
除了计算并存储每个位置两边的最大高度以外,也可以用单调栈计算能接的雨水总量。
维护一个单调栈,单调栈存储的是下标,满足从栈底到栈顶的下标对应的数组height
中的元素递减。
从左到右遍历数组,遍历到下标i
时,如果栈内至少有两个元素,记栈顶元素为
top
,top
的下面一个元素是left
,则一定有height[left] >= height[top]
。如果height[i] > height[top]
,则得到一个可以接雨水的区域,该区域的宽度是i - left - 1
,高度是min(height[left], height[i]) - height[top]
,根据宽度和高度即可计算得到该区域能接的雨水量。
为了得到left
,需要将top
出栈。在对top
计算能接的雨水量之后,left
变成新的top
,重复上述操作,直到栈变为空,或者栈顶下标对应的
height
中的元素大于或等于height[i]
。
在对下标i
处计算能接的雨水量之后,将i
入栈,继续遍历后面的下标,计算能接的雨水量。遍历结束之后即可得到能接的雨水总量。
1 |
|
方法三:双指针
动态规划的做法中,需要维护两个数组leftMax
和rightMax
,因此空间复杂度是O(n)
。是否可以将空间复杂度降到O(1)
?
注意到下标i
处能接的雨水量由leftMax[i]
和rightMax[i]
中的最小值决定。由于数组leftMax
是从左往右计算,数组rightMax
是从右往左计算,因此可以使用双指针和两个变量代替两个数组。
维护两个指针left
和right
,以及两个变量leftMax
和rightMax
,初始时left = 0, right = n − 1, leftMax = 0, rightMax = 0
。指针left
只会向右移动,指针right
只会向左移动,在移动指针的过程中维护两个变量leftMax
和rightMax
的值。
当两个指针没有相遇时,进行如下操作:
使用height[left]
和height[right]
的值更新leftMax
和rightMax
的值;
如果height[left] < height[right]
,则必有leftMax<rightMax
,下标left
处能接的雨水量等于leftMax − height[left]
,将下标left
处能接的雨水量加到能接的雨水总量,然后将left
加1
(即向右移动一位);
如果height[left] ≥ height[right]
,则必有leftMax ≥ rightMax
,下标right
处能接的雨水量等于rightMax − height[right]
,将下标right
处能接的雨水量加到能接的雨水总量,然后将right
减
1
(即向左移动一位)。
当两个指针相遇时,即可得到能接的雨水总量。
1 |
|
以上内容参考于: