这题其实不难,就是有点烦,做法为模拟实际情况,从左到右遍历数组,找出一个“桶”,然后计算盛水量。寻找桶的方式为寻找它的左右边界。
为了逻辑清晰,这里使用了状态机,整体分为三个状态:
- 寻找左边界状态
- 寻找右边界状态
- 寻找最优右边界状态
于是上述三个状态下的行为分别是:
- 向右寻找第一个降序点
- 向右寻找第一个升序点
- 向右寻找更高的右边界
在状态 3 时,成桶的条件有两个:
- 找到了一个比左边界更高的右边界。(不可能更高了,更高就向左边溢出了)
- 没找到最优的右边界,但数组结束了。此时我们取记录下来的右边界计算水,然后把遍历指针回退到这个右边界上。
public long maxWater (int[] arr) {
int leftBound = -1, rightBound = -1;
long sum = 0;
int state = 0;
int index = 0;
while (index < arr.length) {
switch (state) {
case 0: {
// 找到第一个降序邻居,作为左边界
if ((index < arr.length - 1) && (arr[index] > arr[index + 1])) {
leftBound = index;
state = 1;
}
++ index;
break;
}
case 1: {
// 找到第一个升序邻居,作为右边界
if ((index < arr.length - 1) && (arr[index] < arr[index + 1])) {
rightBound = index + 1;
state = 2;
}
++ index;
break;
}
case 2: {
// 比当前右边界更高的边界更优
if ((index < arr.length - 1) && (arr[index + 1] > arr[rightBound])) {
rightBound = index + 1;
}
/**
1. 右边界高于左边界时,此右边界为最优。
2. 没有触发最优右边界条件,但已经到了数组末尾。
取当前记录的最优右边界计算,然后回退到此右边界继续
*/
if ((index == arr.length - 1) || (arr[rightBound] >= arr[leftBound])) {
sum += water(arr, leftBound, rightBound);
state = 0;
index = rightBound;
continue;
}
++ index;
break;
}
default:
break;
}
}
return sum;
}
public long water (int[] arr, int left, int right) {
int min = Math.min(arr[left], arr[right]);
long sum = 0;
for (int i = left + 1; i < right; ++ i) {
if (min > arr[i]) {
sum += min - arr[i];
}
}
return sum;
}



京公网安备 11010502036488号