接雨水问题

346 阅读2分钟

题目

image.png

解答

class Solution {
    public  int trap(int[] height) {
        if (height.length <= 1) {
            return 0;
        }

        int sum = 0;
        Stack<Integer> stack = new Stack();
        stack.push(0);

        for (int i = 1; i < height.length; i ++) {
            while (!stack.empty() && height[i] > height[stack.peek()]) {
                // 如果当前元素比栈顶的元素大, 计算一次水量
                int index = stack.pop();
                if (stack.empty()) {
                    break;
                }
                int preHeight = height[stack.peek()];
                int area = (i - stack.peek() - 1) * (Math.min(preHeight, height[i]) - height[index]);
                sum += area;

            }
            stack.push(i);

        }

        return sum;

    }
}

思路1

(1) 每个位置能接雨水的值, 等于当前位置向左一直寻找的最大值leftMax, 和当前位置向右一直寻找的最大值rightMax有关. 能接的值 = Math.min(leftMax, rightMax) - height[i]

就是积蓄的雨水在当前位置的积水量

(2) 枚举完所有位置, 每次枚举都需要向左, 向右找, 枚举完后, 就得到能接雨水的值

思路2

(1) 利用一个栈, 从左往右遍历数组, 遇见比栈顶元素小的(空的时候就直接添加), 就将数组元素的下标入栈.

(2) 栈中的元素顶部的就是最后添加的元素, 一旦当前元素比栈顶的元素大, 就表明可以进行一次雨水量的计算了.

假设栈里本身是A, B ,C 当前的元素是D, 当D > C时, 将C 弹出, 根据此时的栈顶B和当前元素D就像故意再加上弹出的C就可以计算出C位置的积水量了 = (Math.min(B, D) - h[C]) * (B - D -1)

注意一定要记得减去h[C]

然后就得到了C处的一部分水量, 注意, 一定是一部分.

然后D和B比, 如果D比B大, 同样的弹出B, 然后D和A加上B, 计算一次水量, 注意这里D和A之间的距离就不再是1了, 因为计算出来的水量是横着的一列, 就是在这里, 计算得到的水量, 有一部分就是属于C位置的水量.

也就是下图中1区域就是弹出C的时候计算的水量, 2区域就是弹出B的时候计算的水量

image.png

(3) 当步骤2中, A也弹出后, 无法计算水量, 因为栈顶没有元素了, 此时将D入栈, 继续遍历数组元素, 最后直到数组遍历完毕.