文章目录
- 学习目标:
- 学习内容:
- 解题思路:
- 方法1.动态规划
- 方法2.单调栈
- 方法3.双指针
学习目标:
算法学习
学习内容:
LeetCode第42题-接雨水
给定 n 个非负整数表示每个宽度为 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 * 104
0 <= height[i] <= 105
链接:https://leetcode.cn/problems/trapping-rain-water
解题思路:
通过观察可以发现,每个格子上的雨量=取其左边最大值右边最大值中较小的一个-当前格子的高度,就是这个格子可以接的水量,有点拗口,下面慢慢解释。核心思想就是这句话。所有的解题思路也都是根据这句话来的。
方法1.动态规划
思路:首先考虑暴力做法,找找思路,暴力做法可以遍历数组,在每个位置分别往两边寻找左柱子中的最大高度和右柱子中的最大高度,找到之后,用左右最大高度的较小者减去当前柱子的高度,就是当前位置能接的水量。该方法要循环整个数组,并且每个位置要遍历数组寻找左右柱子高度的最大值,嵌套了一层循环,所以复杂度是O(n^2)。
我们怎样加速嵌套的这层循环呢,其实可以预先计算从左往右和从右往左的最大高度数组,在循环数组的时候,可以直接拿到该位置左右两边的最大高度,当前位置的接水量就是左右两边高度的较小者减去当前位置柱子的高度
复杂度:时间复杂度O(n),寻找左右的最大高度,循环计算每个位置的接水量,总共3个循环,但他们不是嵌套关系。空间复杂度是O(n),n是heights数组,用到了leftMax和rightMax数组,即存放左右两边最大高度的数组。
class Solution {
public int trap(int[] height) {
int n = height.length;
if (n == 0) {
return 0;
}
int[] leftMax = new int[n];
leftMax[0] = height[0];
for (int i = 1; i < n; ++i) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
int[] rightMax = new int[n];
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return ans;
}
}
方法2.单调栈
思路:遍历heights数组,将其中的元素加入单调递减栈,如果当前柱子的高度大于栈顶柱子的高度, 不断出栈,相当于找到左边比当前柱子矮的位置,然后每次出栈之后都要累加一下面积。
复杂度:时间复杂度O(n),n是heights的长度,数组中的每个元素最多入栈出栈一次。空间复杂度O(n),栈的空间,最多不会超过heights的长度
class Solution {
public int trap(int[] height) {
int n = height.length;
if (n == 0) {
return 0;
}
int[] leftMax = new int[n];
leftMax[0] = height[0];
for (int i = 1; i < n; ++i) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
int[] rightMax = new int[n];
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return ans;
}
}
方法3.双指针
思路:如果右边存在一个比当前高的柱子,就会形成一个洼地,同理,左边存在一个比当前高柱子,也会形成一个坑,用双指针循环heights数组,判断是否形成洼地,如果能形成洼地,则计算积水量,累加进ans。
复杂度:时间复杂度O(n),n为heights的长度, 总共遍历heights一次。空间复杂度O(1),只用到了两个指针
class Solution {
public int trap(int[] height) {
int ans = 0;
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
}
}
解题思路均是引用下面连接。有兴趣可以去原连接看一看,作者讲的很好。
作者:xiaochen1024
链接:https://leetcode.cn/problems/trapping-rain-water/solution/42-jie-yu-shui-by-chen-wei-f-ll1n/