11## 题目地址
2+
23https://leetcode.com/problems/trapping-rain-water/description/
34
45## 题目描述
56
6-
77```
88Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
99
@@ -23,41 +23,48 @@ Output: 6
2323
2424```
2525
26- ## 思路
26+ ## 前置知识
27+
28+ - 空间换时间
29+ - 双指针
30+ - 单调栈
31+
32+ ## 双数组
33+
34+ ### 思路
2735
2836这是一道雨水收集的问题, 难度为` hard ` . 如图所示,让我们求下过雨之后最多可以积攒多少的水。
2937
30- 如果采用暴力求解的话,思路应该是height数组依次求和 ,然后相加。
38+ 如果采用暴力求解的话,思路应该是 height 数组依次求和 ,然后相加。
3139
3240伪代码:
3341
3442``` js
35-
36- for (let i = 0 ; i < height .length ; i++ ) {
37- area += (h[i] - height[i]) * 1 ; // h为下雨之后的水位
43+ for (let i = 0 ; i < height .length ; i++ ) {
44+ area += (h[i] - height[i]) * 1 ; // h为下雨之后的水位
3845}
39-
4046```
41- 问题转化为求h,那么h[ i] 又等于` 左右两侧柱子的最大值中的较小值 ` ,即
47+
48+ 问题转化为求 h,那么 h[ i] 又等于` 左右两侧柱子的最大值中的较小值 ` ,即
4249` h[i] = Math.min(左边柱子最大值, 右边柱子最大值) `
4350
44- 如上图那么h为 [ 0, 1, 1, 2, 2, 2 ,2, 3, 2, 2, 2, 1]
51+ 如上图那么 h 为 [ 0, 1, 1, 2, 2, 2 ,2, 3, 2, 2, 2, 1]
4552
4653问题的关键在于求解` 左边柱子最大值 ` 和` 右边柱子最大值 ` ,
4754我们其实可以用两个数组来表示` leftMax ` , ` rightMax ` ,
48- 以leftMax为例,leftMax[ i] 代表i的左侧柱子的最大值,因此我们维护两个数组即可。
49- ## 关键点解析
55+ 以 leftMax 为例,leftMax[ i] 代表 i 的左侧柱子的最大值,因此我们维护两个数组即可。
56+
57+ ### 关键点解析
5058
51- - 建模 ` h[i] = Math.min(左边柱子最大值, 右边柱子最大值) ` (h为下雨之后的水位 )
59+ - 建模 ` h[i] = Math.min(左边柱子最大值, 右边柱子最大值) ` (h 为下雨之后的水位 )
5260
53- ## 代码
61+ ### 代码
5462
55- 代码支持 JavaScript,Python3:
63+ 代码支持 JavaScript,Python3,C++ :
5664
5765JavaScript Code:
5866
5967``` js
60-
6168/*
6269 * @lc app=leetcode id=42 lang=javascript
6370 *
@@ -68,29 +75,28 @@ JavaScript Code:
6875 * @param {number[]} height
6976 * @return {number}
7077 */
71- var trap = function (height ) {
72- let max = 0 ;
73- let volumn = 0 ;
74- const leftMax = [];
75- const rightMax = [];
76-
77- for (let i = 0 ; i < height .length ; i++ ) {
78- leftMax[i] = max = Math .max (height[i], max);
79- }
78+ var trap = function (height ) {
79+ let max = 0 ;
80+ let volumn = 0 ;
81+ const leftMax = [];
82+ const rightMax = [];
8083
81- max = 0 ;
84+ for (let i = 0 ; i < height .length ; i++ ) {
85+ leftMax[i] = max = Math .max (height[i], max);
86+ }
8287
83- for (let i = height .length - 1 ; i >= 0 ; i-- ) {
84- rightMax[i] = max = Math .max (height[i], max);
85- }
88+ max = 0 ;
8689
87- for (let i = 0 ; i < height . length ; i++ ) {
88- volumn = volumn + Math .min (leftMax [i], rightMax[i]) - height[i]
89- }
90+ for (let i = height . length - 1 ; i >= 0 ; i-- ) {
91+ rightMax[i] = max = Math .max (height [i], max);
92+ }
9093
91- return volumn;
92- };
94+ for (let i = 0 ; i < height .length ; i++ ) {
95+ volumn = volumn + Math .min (leftMax[i], rightMax[i]) - height[i];
96+ }
9397
98+ return volumn;
99+ };
94100```
95101
96102Python Code:
@@ -107,9 +113,130 @@ class Solution:
107113 r[i] = max (r[i + 1 ], heights[i])
108114 for i in range (len (heights)):
109115 ans += max (0 , min (l[i + 1 ], r[i]) - heights[i])
110- return ans
116+ return ans
111117```
112118
119+ C++ Code:
120+
121+ ``` c++
122+ int trap (vector<int >& heights)
123+ {
124+ if(heights == null)
125+ return 0;
126+ int ans = 0;
127+ int size = heights.size();
128+ vector<int > left_max(size), right_max(size);
129+ left_max[ 0] = heights[ 0] ;
130+ for (int i = 1; i < size; i++) {
131+ left_max[ i] = max(heights[ i] , left_max[ i - 1] );
132+ }
133+ right_max[ size - 1] = heights[ size - 1] ;
134+ for (int i = size - 2; i >= 0; i--) {
135+ right_max[ i] = max(heights[ i] , right_max[ i + 1] );
136+ }
137+ for (int i = 1; i < size - 1; i++) {
138+ ans += min(left_max[ i] , right_max[ i] ) - heights[ i] ;
139+ }
140+ return ans;
141+ }
142+
143+ ```
144+
145+ **复杂度分析**
146+
147+ - 时间复杂度:$O(N)$
148+ - 空间复杂度:$O(N)$
149+
150+ ## 双指针
151+
152+ ### 思路
153+
154+ 上面代码比较好理解,但是需要额外的 \${N} 的空间。从上面解法可以看出,我们实际上只关心左右两侧较小的那一个,并不需要两者都计算出来。具体来说:
155+
156+ - 如果 l[i + 1] < r[i] 那么 最终积水的高度由 i 的左侧最大值决定。
157+ - 如果 l[i + 1] >= r[i] 那么 最终积水的高度由 i 的右侧最大值决定。
158+
159+ 因此我们不必维护完整的两个数组,而是可以只进行一次遍历,同时维护左侧最大值和右侧最大值,使用常数变量完成即可。这是一个典型的双指针问题,
160+
161+ 具体算法:
162+
163+ 1. 维护两个指针 left 和 right,分别指向头尾。
164+ 2. 初始化左侧和右侧最低的高度都为 0。
165+ 3. 比较 height[left] 和 height[right]
166+
167+ - 3.1 如果 height[left] < height[right]
168+ - 3.1.1 如果 height[left] >= left_max, 则当前格子积水面积为(left_max - height[left])
169+ - 3.1.2 否则无法积水,即积水面积为 0
170+ - 3.2 左指针右移一位
171+
172+ - 3.3 如果 height[left] >= height[right]
173+ - 3.3.1 如果 height[right] >= right_max, 则当前格子积水面积为(right_max - height[right])
174+ - 3.3.2 否则无法积水,即积水面积为 0
175+ - 3.4 右指针左移一位
176+
177+ ### 代码
178+
179+ 代码支持 Python3,C++:
180+
181+ ```python
182+ class Solution:
183+ def trap(self, heights: List[int]) -> int:
184+ n = len(heights)
185+ l_max = r_max = 0
186+ l, r = 0, n - 1
187+ ans = 0
188+ while l < r:
189+ if heights[l] < heights[r]:
190+ if heights[l] < l_max:
191+ ans += l_max - heights[l]
192+ else:
193+ l_max = heights[l]
194+ l += 1
195+ else:
196+ if heights[r] < r_max:
197+ ans += r_max - heights[r]
198+ else:
199+ r_max = heights[r]
200+ r -= 1
201+ return ans
202+ ```
203+
204+ ``` c++
205+
206+ class Solution {
207+ public:
208+ int trap(vector<int >& heights)
209+ {
210+ int left = 0, right = heights.size() - 1;
211+ int ans = 0;
212+ int left_max = 0, right_max = 0;
213+ while (left < right) {
214+ if (heights[left] < heights[right]) {
215+ heights[left] >= left_max ? (left_max = heights[left]) : ans += (left_max - heights[left]);
216+ ++left;
217+ }
218+ else {
219+ heights[right] >= right_max ? (right_max = heights[right]) : ans += (right_max - heights[right]);
220+ --right;
221+ }
222+ }
223+ return ans;
224+ }
225+
226+ };
227+ ```
228+
229+ **复杂度分析**
230+
231+ - 时间复杂度:$O(N)$
232+ - 空间复杂度:$O(1)$
233+
113234## 相关题目
114235
115236- [84.largest-rectangle-in-histogram](https://github.com/azl397985856/leetcode/blob/master/problems/84.largest-rectangle-in-histogram.md)
237+
238+ 更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 30K star 啦。
239+
240+ 关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
241+
242+ 
0 commit comments