LeetCode-[数组]-接雨水(三种解法)
题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
1. 暴力解法
-
O(N^2) 遍历数组,找出 i 左右两边的最大值,计算出 i 位置能存的水
public int trap(int[] height) { if (height == null || height.length < 3) { return 0; } int res = 0; for (int i = 1; i < height.length - 1; i++) { int leftMax = 0, rightMax = 0; //定义在for里,因为每次都要更新 for (int j = 0; j < i; j++) { //i 左边最大值 leftMax = Math.max(leftMax, height[j]); } for (int j = height.length - 1; j > i; j--) { //i 右边最大值 rightMax = Math.max(rightMax, height[j]); } //与0比较:负数不要,说明i位置的柱子比左右两边都突出 res += Math.max(0, Math.min(leftMax, rightMax) - height[i]); } return res; }
2. 帮助数组
-
时复、空复:O(N) 对数组进行预处理,把数组i每个位置的左右两边的最大值都记录下来(从左往右和从右往左) 这样就剩下暴力法中每次都要遍历找最大值的功夫
public int trap(int[] height) { if (height == null || height.length < 3) { return 0; } int res = 0; int n = height.length - 2; //头尾不用比较 int[] leftMaxs = new int[n]; int[] rightMaxs = new int[n]; leftMaxs[0] = height[0]; rightMaxs[n - 1] = height[n + 1]; for (int i = 1; i < n; i++) { //生成 左——>右 的最大值数组 leftMaxs[i] = Math.max(leftMaxs[i - 1], height[i]); } for (int i = n - 2; i >= 0; i--) { //生成 右——>左 的最大值数组。下标比较绕,但可以想明白 rightMaxs[i] = Math.max(rightMaxs[i + 1], height[i + 2]); } for (int i = 1; i <= n; i++) { //遍历数组,帮助数组可以方便的确定 i 位置左右两边的最大值 res += Math.max(0, Math.min(leftMaxs[i - 1], rightMaxs[i - 1]) - height[i]); } return res; }
3. 最优解——双指针
-
时间复杂度:O(N) 空间复杂度:O(1) 两个帮助数组可以用双指针+双变量优化,leftMax、rightMax分别记录 l 左右两边的最大值 在 l、r 遍历过程中,遇到更大值就更新 leftMax、rightMax
public int trap(int[] height) { if (height == null || height.length < 3) { return 0; } int res = 0; int l = 1, r = height.length - 2; //双指针 int leftMax = height[0], rightMax = height[height.length - 1]; //双变量 while (l <= r) { if (leftMax <= rightMax) { res += Math.max(0, leftMax - height[l]); leftMax = Math.max(leftMax, height[l++]); //更新左边的最大值 } else { res += Math.max(0, rightMax - height[r]); rightMax = Math.max(rightMax, height[r--]); //更新右边的最大值 } } return res; }