Update
This commit is contained in:
@@ -36,9 +36,9 @@
|
||||
* 动态规划
|
||||
* 单调栈
|
||||
|
||||
## 双指针解法
|
||||
## 暴力解法
|
||||
|
||||
这道题目使用双指针法并不简单,我们来看一下思路。
|
||||
本题暴力解法也是也是使用双指针。
|
||||
|
||||
首先要明确,要按照行来计算,还是按照列来计算。
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。
|
||||
|
||||
首先从头遍历所有的列,并且**要注意第一个柱子和最后一个柱子不接雨水**,代码如下:
|
||||
|
||||
```CPP
|
||||
for (int i = 0; i < height.size(); i++) {
|
||||
// 第一个柱子和最后一个柱子不接雨水
|
||||
@@ -129,19 +130,18 @@ public:
|
||||
};
|
||||
```
|
||||
|
||||
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。
|
||||
空间复杂度为O(1)。
|
||||
因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2),空间复杂度为O(1)。
|
||||
|
||||
力扣后面修改了后台测试数据,所以以上暴力解法超时了。
|
||||
|
||||
## 双指针优化
|
||||
|
||||
|
||||
|
||||
|
||||
## 动态规划解法
|
||||
|
||||
在上一节的双指针解法中,我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。
|
||||
在暴力解法中,我们可以看到只要记录左边柱子的最高高度 和 右边柱子的最高高度,就可以计算当前位置的雨水面积,这就是通过列来计算。
|
||||
|
||||
当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度。
|
||||
|
||||
为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight)。这样就避免了重复计算,这就用到了动态规划。
|
||||
为了得到两边的最高高度,使用了双指针来遍历,每到一个柱子都向两边遍历一遍,这其实是有重复计算的。我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),这样就避免了重复计算。
|
||||
|
||||
当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
|
||||
|
||||
@@ -149,8 +149,6 @@ public:
|
||||
|
||||
从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
|
||||
|
||||
这样就找到递推公式。
|
||||
|
||||
代码如下:
|
||||
|
||||
```CPP
|
||||
@@ -185,10 +183,13 @@ public:
|
||||
|
||||
## 单调栈解法
|
||||
|
||||
这个解法可以说是最不好理解的了,所以下面我花了大量的篇幅来介绍这种方法。
|
||||
关于单调栈的理论基础,单调栈适合解决什么问题,单调栈的工作过程,大家可以先看这题讲解 [739. 每日温度](https://programmercarl.com/0739.每日温度.html)。
|
||||
|
||||
单调栈就是保持栈内元素有序。和[栈与队列:单调队列](https://programmercarl.com/0239.滑动窗口最大值.html)一样,需要我们自己维持顺序,没有现成的容器可以用。
|
||||
|
||||
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。
|
||||
|
||||
而接雨水这道题目,我们正需要寻找一个元素,右边最大元素以及左边最大元素,来计算雨水面积。
|
||||
|
||||
### 准备工作
|
||||
|
||||
@@ -212,6 +213,7 @@ public:
|
||||
|
||||

|
||||
|
||||
关于单调栈的顺序给大家一个总结: [739. 每日温度](https://programmercarl.com/0739.每日温度.html) 中求一个元素右边第一个更大元素,单调栈就是递增的,[84.柱状图中最大的矩形](https://programmercarl.com/0084.柱状图中最大的矩形.html)求一个元素右边第一个更小元素,单调栈就是递减的。
|
||||
|
||||
3. 遇到相同高度的柱子怎么办。
|
||||
|
||||
@@ -227,13 +229,13 @@ public:
|
||||
|
||||
4. 栈里要保存什么数值
|
||||
|
||||
是用单调栈,其实是通过 长 * 宽 来计算雨水面积的。
|
||||
使用单调栈,也是通过 长 * 宽 来计算雨水面积的。
|
||||
|
||||
长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,
|
||||
|
||||
那么栈里有没有必要存一个pair<int, int>类型的元素,保存柱子的高度和下标呢。
|
||||
|
||||
其实不用,栈里就存放int类型的元素就行了,表示下标,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。
|
||||
其实不用,栈里就存放下标就行,想要知道对应的高度,通过height[stack.top()] 就知道弹出的下标对应的高度了。
|
||||
|
||||
所以栈的定义如下:
|
||||
|
||||
@@ -243,9 +245,17 @@ stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度
|
||||
|
||||
明确了如上几点,我们再来看处理逻辑。
|
||||
|
||||
### 单调栈处理逻辑
|
||||
### 单调栈处理逻辑
|
||||
|
||||
先将下标0的柱子加入到栈中,`st.push(0);`。
|
||||
以下操作过程其实和 [739. 每日温度](https://programmercarl.com/0739.每日温度.html) 也是一样的,建议先做 [739. 每日温度](https://programmercarl.com/0739.每日温度.html)。
|
||||
|
||||
以下逻辑主要就是三种情况
|
||||
|
||||
* 情况一:当前遍历的元素(柱子)高度小于栈顶元素的高度 height[i] < height[st.top()]
|
||||
* 情况二:当前遍历的元素(柱子)高度等于栈顶元素的高度 height[i] == height[st.top()]
|
||||
* 情况三:当前遍历的元素(柱子)高度大于栈顶元素的高度 height[i] > height[st.top()]
|
||||
|
||||
先将下标0的柱子加入到栈中,`st.push(0);`。 栈中存放我们遍历过的元素,所以先将下标0加进来。
|
||||
|
||||
然后开始从下标1开始遍历所有的柱子,`for (int i = 1; i < height.size(); i++)`。
|
||||
|
||||
@@ -278,7 +288,7 @@ if (height[i] == height[st.top()]) { // 例如 5 5 1 7 这种情况
|
||||
|
||||
当前遍历的元素i,就是凹槽右边的位置,下标为i,对应的高度为height[i](就是图中的高度3)。
|
||||
|
||||
此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的三个元素来接水!**
|
||||
此时大家应该可以发现其实就是**栈顶和栈顶的下一个元素以及要入栈的元素,三个元素来接水!**
|
||||
|
||||
那么雨水高度是 min(凹槽左边高度, 凹槽右边高度) - 凹槽底部高度,代码为:`int h = min(height[st.top()], height[i]) - height[mid];`
|
||||
|
||||
@@ -367,7 +377,7 @@ public:
|
||||
|
||||
### Java:
|
||||
|
||||
双指针法
|
||||
暴力解法:
|
||||
```java
|
||||
class Solution {
|
||||
public int trap(int[] height) {
|
||||
@@ -393,7 +403,7 @@ class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
动态规划法
|
||||
双指针:
|
||||
```java
|
||||
class Solution {
|
||||
public int trap(int[] height) {
|
||||
@@ -470,7 +480,7 @@ class Solution {
|
||||
|
||||
### Python:
|
||||
|
||||
双指针法
|
||||
暴力解法:
|
||||
```Python
|
||||
class Solution:
|
||||
def trap(self, height: List[int]) -> int:
|
||||
@@ -490,7 +500,8 @@ class Solution:
|
||||
res += res1
|
||||
return res
|
||||
```
|
||||
动态规划
|
||||
|
||||
双指针:
|
||||
```python
|
||||
class Solution:
|
||||
def trap(self, height: List[int]) -> int:
|
||||
@@ -602,7 +613,7 @@ func trap(height []int) int {
|
||||
}
|
||||
```
|
||||
|
||||
动态规划解法:
|
||||
双指针解法:
|
||||
|
||||
```go
|
||||
func trap(height []int) int {
|
||||
@@ -681,7 +692,7 @@ func min(x, y int) int {
|
||||
### JavaScript:
|
||||
|
||||
```javascript
|
||||
//双指针
|
||||
//暴力解法
|
||||
var trap = function(height) {
|
||||
const len = height.length;
|
||||
let sum = 0;
|
||||
@@ -702,7 +713,7 @@ var trap = function(height) {
|
||||
return sum;
|
||||
};
|
||||
|
||||
//动态规划
|
||||
//双指针
|
||||
var trap = function(height) {
|
||||
const len = height.length;
|
||||
if(len <= 2) return 0;
|
||||
@@ -782,7 +793,7 @@ var trap = function(height) {
|
||||
|
||||
### TypeScript
|
||||
|
||||
双指针法:
|
||||
暴力解法:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
@@ -809,7 +820,7 @@ function trap(height: number[]): number {
|
||||
};
|
||||
```
|
||||
|
||||
动态规划:
|
||||
双指针:
|
||||
|
||||
```typescript
|
||||
function trap(height: number[]): number {
|
||||
|
||||
Reference in New Issue
Block a user