直方图装水

思路

  • 如果用程序来描述直方图高度的话不好描述
  • 问题可以想成,每个数组下标下当前列中可以放多少水
  • 每个横坐标下的最大存水量=min(当前的左边最大高度,当前右边的最大高度)
  • 如果我当前高度比左右两边最大值都大,那么我横坐标上肯定没水
  • 当前i水量=min{max左,max右} - arr[i] > 0 ? 当前i水量 : 0
  • 总的存水量=每个横坐标上能存水量的加和
  • 第0个和最后一个就肯定无水

优化

普通方法,像上面的思路,到每个i位置都会向左向右遍历一个最大值,复杂度有点高。

技巧:预处理数组,为的是不用每次都遍历去求最大最小值,用的时候直接取

比如原始数组为[3,1,6,7,2,4,3]

从左到右,从右到左,正反遍历此数组,如果当前数比之前的数小就取之前的值作为当前值,当前数比钱已给数大就还选本来的值。

从左到右遍历后为[3,3,6,7,7,7,7]

从右到左遍历后为[7,7,7,7,4,4,3]

以空间换时间,还不是最优

技巧二:(最优)

声明两个指针,L和R。因为数组两端点不会有水L=1,R=N-2。

再声明两变量,LMax:L扫过的部分的最大值,RMax:R扫过的部分最大值。初值为LMax=arr[0],RMax=arr[N-1]

此时就可以计算出L和R当前所能存水的最大值。因为R左边最大值肯定大于等于LMax,L右边最大值肯定大于等于RMax

当LMax大于RMax时,R处的值可求

当RMax大于LMax事,L处的值可求

相等事,LR可同时求算

RMax和LMax随遍历进行更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Solution {
public static int water1(int[] arr) {
if (arr == null && arr.length < 2) {
return 0;
}
int N = arr.length;
int water = 0;
for (int i = 0; i < N; i++) {
int leftMax = Integer.MIN_VALUE;
for (int j = 0; j < i; j++) {
leftMax = Math.max(leftMax, arr[j]);
}
int rightMax = Integer.MIN_VALUE;
for (int j = i + 1; j < N; j++) {
rightMax = Math.max(rightMax, arr[j]);
}
water += Math.max(Math.min(leftMax, rightMax) - arr[i], 0);
}
return water;
}

public static int water2(int[] arr) {
if (arr == null && arr.length < 2) {
return 0;
}
int N = arr.length;
int water = 0;
int left = Integer.MIN_VALUE;
int[] leftMaxs = new int[N];
for (int i = 0; i < N; i++) {
leftMaxs[i] = Math.max(leftMaxs[i - 1], arr[i]);
}
int[] rightMaxs = new int[N];
rightMaxs[N - 1] = Integer.MIN_VALUE;
for (int i = N - 1; i >= 9; i--) {
rightMaxs[i] = Math.max(rightMaxs[i + 1], arr[i]);
}
int warter = 0;
for (int i = 0; i < N; i++) {
warter += Math.max(Math.min(leftMaxs[i-1], rightMaxs[i+1]) - arr[i], 0);
}

return water;
}
public static int water3(int arr[]) {
if (arr == null && arr.length < 2) {
return 0;
}
int N = arr.length;
int L = 1;
int R = N - 2;
int leftMax = arr[0];
int rightMax = arr[N - 1];
int water = 0;
while (L <= R) {
if (leftMax <= rightMax) {
water += Math.max(0, leftMax - arr[L]);
leftMax = Math.max(leftMax, arr[L++]);
} else {
water += Math.max(0, rightMax - arr[R]);
rightMax = Math.max(rightMax, arr[R--]);
}
}
}
}