这题其实不难,就是有点烦,做法为模拟实际情况,从左到右遍历数组,找出一个“桶”,然后计算盛水量。寻找桶的方式为寻找它的左右边界。
为了逻辑清晰,这里使用了状态机,整体分为三个状态:
- 寻找左边界状态
- 寻找右边界状态
- 寻找最优右边界状态
于是上述三个状态下的行为分别是:
- 向右寻找第一个降序点
- 向右寻找第一个升序点
- 向右寻找更高的右边界
在状态 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; }