例题: 接雨水
接雨水是一道高频面试题,而其中最经典的解法就是单调栈,详细的讲解可以查看这篇文章,这里只记录几个要点。
1. 分析:
a. 使用单调栈法求雨水的容量,思路是按行进行计算,如下图所示:
b. 单调栈中的元素是柱子的索引,而不是柱子的高度。
为啥呢?因为要求水槽的宽度时,需要通过索引来计算;而柱子的高度也可以通过索引获取到,最终水槽的容量就是底乘以高。
c. 单调栈中的元素顺序是从小到大,即栈顶元素对应的高度最小。
这样一旦发现柱子高于栈顶元素了,则说明出现水槽了。
此时,栈顶元素就是水槽底部的索引,栈顶元素的下一个元素就是水槽左边柱子的索引,当前索引就是水槽右边柱子的索引。
d. 如果遇到相同高度的柱子,则将之前的索引出栈,将当前索引入栈。
因为在相同高度的柱子中,接雨水是按照最右侧的高度来接水的。
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;
}