leecode42 接雨水
总结
这个题感觉好难,我想不到什么好的方法,刚开始想的和后续做的只能是暴力解法,虽然做出来了,但是时间复杂度很高,最后膜拜了这个灵神的做法,灵神还是太超模了,感觉和我这个菜鸟不在一个水平
不过我虽然做的没有灵神巧妙,我的做法也是有一定的可取之处的,我的思路中最关键的一步就是对已经统计过的柱子进行抬高,防止下一轮重复。
灵神的思路主要是通过单独设置一个左挡板和右挡板对每一个柱子进行计算,最后取和。
我的思路
-
定义一个左指针和右指针,利用这两个指针进行遍历
- 首先先找到这个左指针和右指针指向位置不为0的下标,
- 然后我们找出这两个位置中的最小值,则我们可以先把这个中间的位置能存多少水算出来,
统计完之后将这个柱子抬高吗,抬高是关键,因为后续可能还会统计到这个位置,必须把上一次统计造成的影响消除
-
代码如下所示
public int trap(int[] height) {int len = height.length;int left = 0, right = len - 1;int ans = 0;int min = 0; while (left < right) {if (height[left] == 0) {left++;continue;}if (height[right] == 0) {right--;continue;}int newMin = Math.min(height[left], height[right]);if (min < newMin) {min = newMin;}// 直接算当前区间所有 < Min 的柱子for (int i = left + 1; i < right; i++) {if (height[i] < newMin) {ans += newMin - height[i];//把这些柱子“虚拟抬高”到 newMin,下一轮不会重复加height[i] = newMin; }}// 指针移动规则不变if (height[left] < height[right])left++;elseright--;}return ans;
}
灵神的思路
灵神的思路是对于每一个块进行分析,
灵神第一种解法,利用两个数组进行记录
public int trap(int[] height) {int len = height.length;int[] left_max = new int[len];int[] right_max = new int[len];left_max[0] = height[0];for(int i = 1;i<len;i++){left_max[i] = Math.max(left_max[i-1],height[i]);}right_max[len-1]=height[len-1];for(int i=len-2;i>=0;i--){right_max[i]=Math.max(right_max[i+1],height[i]);}int ans=0;for(int i=0;i<len;i++){ans+=Math.min(left_max[i],right_max[i])-height[i];}return ans;
}
灵神第二种解法,不用额外数组
- 这个思路的关键就在于,每次当左边的挡板小于右边的挡板的时候,说明它只能够接左边挡板那么高的水,同理,当右边的挡板大于左边的挡板的时候,说明它只能够能够接右边挡板那么高的水
class Solution {public int trap(int[] height) {int ans = 0;int preMax = 0; // 前缀最大值,随着左指针 left 的移动而更新int sufMax = 0; // 后缀最大值,随着右指针 right 的移动而更新int left = 0;int right = height.length - 1;while (left < right) {preMax = Math.max(preMax, height[left]);sufMax = Math.max(sufMax, height[right]);if (preMax < sufMax) {ans += preMax - height[left];left++;} else {ans += sufMax - height[right];right--;}}return ans;}
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/trapping-rain-water/solutions/1974340/zuo-liao-nbian-huan-bu-hui-yi-ge-shi-pin-ukwm/
来源:力扣(LeetCode)