Skip to content

Commit 750635b

Browse files
authored
Merge pull request MisterBooo#77 from xiaoshuai96/master
solved @xiaoshuai96
2 parents 7d07b09 + 53e1681 commit 750635b

File tree

6 files changed

+134
-0
lines changed

6 files changed

+134
-0
lines changed

0042-Trap/Animation/0042-Trap.gif

7.33 MB
Loading

0042-Trap/Animation/01.png

7.52 KB
Loading

0042-Trap/Animation/02.png

7.9 KB
Loading

0042-Trap/Animation/03.png

7.88 KB
Loading

0042-Trap/Animation/resource.png

7.66 KB
Loading

0042-Trap/Article/0042-Trap.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
2+
>
3+
> 个人博客:https://www.zhangxiaoshuai.fun
4+
5+
**本题选择leetcode中第42题,hard级别,目前通过率50.8%#**
6+
7+
### 题目描述:
8+
9+
```txt
10+
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
11+
示例:
12+
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
13+
输出: 6
14+
```
15+
16+
![](../Animation/resource.png)
17+
18+
### 题目分析:
19+
20+
通过题意,一个“凹槽”可以存储的雨水的容量取决于它前后的柱子。
21+
22+
### 解法一:
23+
24+
仔细想想,其实这跟木桶原理是有相似的地方的,针对每一个柱子,我们需要往前看和往后看,分别找出当前柱子前面最高的柱子和后面最高的柱子。
25+
26+
这里有**三种情况**我们需要了解:
27+
28+
- **当前柱子小于前后两个柱子中最矮的那个**
29+
![01](../Animation/01.png)
30+
31+
**当前位置可以存储的雨水容量 = leftMax - curr = 1**
32+
33+
34+
35+
- **当前柱子等于前后两个柱子中最矮的那个**
36+
![02](../Animation/02.png)
37+
38+
**当前位置可以存储的雨水容量为0**
39+
40+
41+
42+
- **当前柱子大于前后两个柱子中最矮的那个**![03](../Animation/03.png)
43+
44+
**因为curr < leftMax,所以当前位置无法存储雨水**
45+
46+
**GIF动画演示:**
47+
48+
![gif01](../Animation/0042-trap.gif)
49+
50+
### 代码:
51+
52+
```java
53+
public int trap02(int[] height) {
54+
int sum = 0;
55+
//最两端的列不用考虑,因为一定不会有水。所以下标从 1 到 length - 2
56+
for (int i = 1; i < height.length - 1; i++) {
57+
int max_left = 0;
58+
//找出左边最高
59+
for (int j = i - 1; j >= 0; j--) {
60+
if (height[j] > max_left) {
61+
max_left = height[j];
62+
}
63+
}
64+
int max_right = 0;
65+
//找出右边最高
66+
for (int j = i + 1; j < height.length; j++) {
67+
if (height[j] > max_right) {
68+
max_right = height[j];
69+
}
70+
}
71+
//找出两端较小的
72+
int min = Math.min(max_left, max_right);
73+
//只有较小的一段大于当前列的高度才会有水,其他情况不会有水
74+
if (min > height[i]) {
75+
sum = sum + (min - height[i]);
76+
}
77+
}
78+
return sum;
79+
}
80+
```
81+
82+
可以看到,上面方法的时间复杂度达到了**O(n^2)**
83+
84+
**那么有没有更好的办法来解决这个问题?**
85+
86+
下面的方法巧妙的使用了**双指针**来解决问题:
87+
88+
与上述解法的思路大致是相同的,都是单个地求出当前墙可以存储雨水的容量;这种解法也是非常的巧妙,是在浏览解题区的时候碰见的,大佬还做了视频(链接放在文末),讲解的非常清楚,我大概用自己的思路来作一文字叙述:
89+
90+
既然使用的是**twoPointers**的思路,那么我们需要分别从数组的最前面和最后面开始,这两个指针是互不影响,都是各走各的,但是如何确定当前指针走过的地方能存放多少雨水量呢?
91+
92+
这个时候,我们就需要两块挡板**leftMax****rightMax**,这两块挡板最开始都是挡在最外面的墙边,随着两个指针前进,**leftMax**代表的是**left**走过的路中最高的墙,**rightMax**同理。
93+
94+
**那么如何计算雨水量呢?**
95+
96+
比较左右两个挡板的高度,然后根据两个挡板各自的指针配合计算。
97+
98+
- 如果左边挡板的高度小于右边的挡板高度,那么左边指针之前的雨水量取决于**leftMax**和height[left]的大小关系,如果前者大于后者,那么容量等与前者减去后者;反之,容量为0(可以参考解法一中的图来理解)
99+
- 如果左边挡板的高度大于等于右边挡板的高度,与上一种情况基本相同,只不过是求的右边的雨水量。
100+
- 在每次移动指针之后,我们要将挡板更新到最大值。
101+
102+
**其实道理也是比较简单,用宏观的思维去看待整个问题,最起码先保证两边的墙的高度(两块挡板),然后依次去到其中各个墙之间能装多少雨水的问题上。(求每次更新最高的挡板和指针指向的墙之间可以存储的雨水量)**
103+
104+
### 代码:
105+
106+
```java
107+
public int trap(int[] height) {
108+
if (height.length == 0) return 0;
109+
int left = 0;
110+
int right = height.length-1;
111+
int leftMax = 0;
112+
int rightMax = 0;
113+
int result = 0;
114+
while (left <= right) {
115+
if (leftMax < rightMax) {
116+
result += leftMax - height[left] > 0 ?
117+
leftMax - height[left] : 0;
118+
leftMax = Math.max(leftMax, height[left]);
119+
left++;
120+
} else {
121+
result += rightMax - height[right] > 0 ?
122+
rightMax - height[right] : 0;
123+
rightMax = Math.max(rightMax, height[right]);
124+
right--;
125+
}
126+
}
127+
return result;
128+
}
129+
```
130+
131+
**时间复杂度:O(n) 空间复杂度:O(1)**
132+
133+
[leetcode配套视频入口](https://leetcode-cn.com/problems/trapping-rain-water/solution/javashi-pin-jiang-jie-xi-lie-trapping-rain-water-b/)
134+

0 commit comments

Comments
 (0)