Leetcode刷题总结 —— 单调栈

188 阅读2分钟

例题: 接雨水

接雨水是一道高频面试题,而其中最经典的解法就是单调栈,详细的讲解可以查看这篇文章,这里只记录几个要点。

1. 分析:

a. 使用单调栈法求雨水的容量,思路是按行进行计算,如下图所示:

image.png

b. 单调栈中的元素是柱子的索引,而不是柱子的高度。
为啥呢?因为要求水槽的宽度时,需要通过索引来计算;而柱子的高度也可以通过索引获取到,最终水槽的容量就是底乘以高。

c. 单调栈中的元素顺序是从小到大,即栈顶元素对应的高度最小。
这样一旦发现柱子高于栈顶元素了,则说明出现水槽了。
此时,栈顶元素就是水槽底部的索引,栈顶元素的下一个元素就是水槽左边柱子的索引,当前索引就是水槽右边柱子的索引。

image.png

d. 如果遇到相同高度的柱子,则将之前的索引出栈,将当前索引入栈。
因为在相同高度的柱子中,接雨水是按照最右侧的高度来接水的。

image.png

e. 当栈为空时,说明已经到了最左边的柱子,此时肯定不能接水了

2. 实现:

public int trap(int[] height) {
    LinkedList<Integer> stack = new LinkedList<>();
    stack.push(0);
    int ans = 0;

    for (int i = 1; i < height.length; i++) {
        if (height[i] < height[stack.peek()]) {
            // 如果当前柱子比栈顶柱子矮,则直接压入栈中
            stack.push(i);
        } else if (height[i] == height[stack.peek()]) {
            // 如果当前柱子与栈顶柱子一样高,则弹出之前的索引,将新索引入栈
            stack.pop();
            stack.push(i);
        } else {
            // 如果当前柱子比栈顶柱子高,说明出现了凹槽,可以接雨水了
            while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
                // 获取水槽底的高度
                Integer bottle = stack.pop();
                // 如果stack为空,说明是最左边的柱子了,接不了雨水
                if (stack.isEmpty()) {
                    break;
                }
                // 获取左边柱子的高度
                Integer left = stack.peek();
                // 水槽的高度等于左右侧柱子中较矮的高度,减去水槽底的高度
                int h = Math.min(height[i], height[left]) - height[bottle];
                // 水槽的宽度等于索引的差
                int w = i - left - 1;
                // 增加容量
                ans += h * w;
            }
            stack.push(i);
        }
    }
    return ans;
}