algorithm-base/animation-simulation/单调队列单调栈/接雨水.md
2021-07-28 02:26:32 +08:00

152 lines
8.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> 如果阅读时发现错误或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
>
> 感谢支持该仓库会一直维护希望对各位有一丢丢帮助
>
> 另外希望手机阅读的同学可以来我的 <u>[**公众号袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
#### [42. 接雨水](https://leetcode-cn.com/problems/trapping-rain-water/)
这道接雨水也是一道特别经典的题目一道必刷题目我们也用单调栈来解决下面我们来看一下题目吧
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图计算按此排列的柱子下雨之后能接多少雨水
示例 1
```
输入height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出6
```
示例 2
```
输入height = [4,2,0,3,2,5]
输出9
```
示例 3
```
输入[4,3,2,0,1,1,5]
输出13
```
> 上面是由数组 [4,3,2,0,1,1,5]表示的高度图在这种情况下可以接 13 个单位的雨水见下图
### 题目解析
看了上面的示例刚开始刷题的同学可能有些懵逼那我们结合图片来理解一下我们就用示例 3 的例子进行举例他的雨水到底代表的是什么
![](https://img-blog.csdnimg.cn/2021032013412768.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
上图则为我们的题目描述是不是理解了呢你也可以这样理解我们在地上放置了若干高度的黄色箱子他们中间有空隙然后我们想在他们里面插入若干蓝色箱子并保证插入之后这些箱子的左视图和右视图都不能看到蓝色箱子
好啦题目我们已经理解了下面我们看一下解题思路做这个这前我们可以先去看一下我们之前做过的另一道题目每日温度这两道题目的思路差不多都是利用了单调栈的思想下面我们来看一下具体思路吧
这里我们也系统的说一下单调栈单调栈含义就是栈内的元素是单调的我们这两个题目用到的都是递减栈相同也可以我们依次将元素压入栈如果当前元素小于等于栈顶元素则入栈如果大于栈顶元素则先将栈顶不断出栈直到当前元素小于或等于栈顶元素为止然后再将当前元素入栈就比如下图的 4想入栈的话则需要 23 出栈之后才能入栈因为 4 大于他俩
<img src="https://img-blog.csdnimg.cn/20210320134154434.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述" style="zoom:80%;" />
我们了解单调栈的含义下面我们来看一下接雨水问题到底该怎么做其实原理也很简单我们通过我们的例 3 来进行说明
首先我们依次入栈 4320 我们的数组前四个元素是符合单调栈规则的但是我们的第五个 1是大于 0 那我们就需要 0 出栈 1 入栈但是我们这样做是为了什么呢有什么意义呢别急我们来看下图
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210320134213324.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
上图我们的4320 已经入栈了我们的另一个元素为 1栈顶元素为 0栈顶下的元素为 2那么我们在这一层接到的雨水数量怎么算呢201 这三个元素可以接住的水为一个单位(见下图)这是我们第一层接到水的数量
能接到水的情况肯定是中间低两边高这样才可以
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210320134228696.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
因为我们需要维护一个单调栈所以我们则需要将 0 出栈 1 入栈那么此时栈内元素为 4321下一位元素为 1我们入栈此时栈内元素为 43211下一元素为 5栈顶元素为 1栈顶的下一元素仍为 1则需要再下一个元素 2那我们求当前层接到的水的数量
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210320134249605.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
我们是通过 2115 这四个元素求得第二层的接水数为 1\*3=3;1 是因为 min(2-1,5-1)=min(1,4)得来的大家可以思考一下木桶效应装水的多少肯定是按最短的那个木板来的所以高度为 13 的话是因为 5 的索引为 62 的索引为 2他们之间共有三个元素345也就是 3 个单位所以为 6-2-1=3
1 出栈之后我们栈顶元素就变成了 2下一元素变成了 3那么 325 这三个元素同样也可以接到水
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210320134307389.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
这是第三层的接水情况能够接到 4 个单位的水下面我们继续出栈 2那么我们的 435 仍然可以接到水啊
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210320134319646.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzODg1OTI0,size_16,color_FFFFFF,t_70)
这是我们第四层接水的情况一共能够接到 5 个单位的水那么我们总的接水数加起来那就是
1+3+4+5=13你学会了吗别急还有视频我们我们再来深入理解一哈
![](https://img-blog.csdnimg.cn/20210319163622150.gif)
### 题目代码
```java
class Solution {
public int trap(int[] height) {
Stack<Integer> stack = new Stack<Integer>();
int water = 0;
//特殊情况
if(height.length <3){
return 0;
}
for(int i = 0; i < height.length; i++){
while(!stack.isEmpty() && height[i] > height[stack.peek()]){
//栈顶元素
int popnum = stack.pop();
//相同元素的情况例11
while(!stack.isEmpty()&&height[popnum] == height[stack.peek()]){
stack.pop();
}
//计算该层的水的单位
if(!stack.isEmpty()){
int temp = height[stack.peek()];//栈顶元素值
//高
int hig = Math.min(temp-height[popnum],height[i]-height[popnum]);
//宽
int wid = i-stack.peek()-1;
water +=hig * wid;
}
}
//这里入栈的是索引
stack.push(i);
}
return water;
}
}
```
GO Code:
```go
func trap(height []int) int {
stack := []int{}
water := 0
// 最左边部分不会接雨水左边持续升高时stack都会弹出所有元素。
for i := 0; i< len(height); i++ {
for len(stack) != 0 && height[i] > height[stack[len(stack) - 1]] {
popnum := stack[len(stack) - 1]
// 出现相同高度的情况其实也可以不用处理如果不处理相同高度时后面的hig为0会产生很多无效的计算
for len(stack) != 0 && height[popnum] == height[stack[len(stack) - 1]] {
stack = stack[:len(stack) - 1]
}
if len(stack) == 0 { break }
le, ri := stack[len(stack) - 1], i
hig := min(height[ri], height[le]) - height[popnum]
wid := ri - le - 1
water += wid * hig
}
stack = append(stack, i)
}
return water
}
func min(a, b int) int {
if a < b { return a }
return b
}
```
###