Leetcode-42.接雨水

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

lc5

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

解法1:两次遍历

思路:

1.第一次遍历找到最高点的位置k

2.第二次先从0到k遍历,记录当前能接住的雨水和sum,

​ 1)首先sum-当前柱子值,然后如果当前值是从0到k最高的柱子则sum+=(height-max)*(k-i)即当前位置到最高柱子能存的雨水数

3.同样从n-1到k遍历,与第二部相似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public:
int trap(vector<int>& height) {
int n=height.size();
if(n<=2)
return 0;
int i;
int max=-1;
int k;
for(i=0;i<n;i++)
{
if(max<height[i])
{
max=height[i];
k=i;
}
}
max=0;
int sum=0;
for(i=0;i<k;i++)
{
sum-=height[i];
if(height[i]>max)
{
sum+=(height[i]-max)*(k-i);
max=height[i];

}
}

max=0;
for(i=n-1;i>k;i--)
{
sum-=height[i];
if(height[i]>max)
{
sum+=(height[i]-max)*(i-k);
max=height[i];

}
}
return sum;
}
};

时间复杂度:O(n)

空间复杂度:O(1)

解法2:动态规划

思路:

1.对于每一列我们求它左边最高的墙,和它右边最高的墙,我们可以用两个数组max_left[i]表示第i列左边墙最高墙的高度,max_right[i]表示右边最高墙的高度,那么i位置能存的水就是则可以这些写:

mins=min(max_left[i],max_right[i]);if(mins>height[i]) i位置的水为mins-height[i];

对应max_left我们可以这样求max_left[i]=max(max_left[i-1],height[i-1])。它前边的墙左边的最高墙的高度,和它前边的墙的高度取一个较大值,同理max_right[i]=max(max_left[i+1],height[i+1])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
int n=height.size();
vector<int> max_left(n,0);
vector<int> max_right(n,0);
int i;
for(i=1;i<n-1;i++)
{
max_left[i]=max(max_left[i-1],height[i-1]);
}
for(i=n-2;i>0;i--)
{
max_right[i]=max(max_right[i+1],height[i+1]);
}
for(i=1;i<n-1;i++)
{
int mins=min(max_left[i],max_right[i]);
if(mins>height[i])
sum+=mins-height[i];
}
return sum;
}
};

时间复杂度:O(n)

空间复杂度:O(n)

解法3:双指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public:
int trap(vector<int>& height) {
int sum = 0;
int max_left = 0;
int max_right = 0;
int left = 1;
int n=height.size();
int right =n- 2; // 加右指针进去
for (int i = 1; i < n - 1; i++)
{
//从左到右更
if (height[left - 1] < height[right + 1])
{
max_left =max(max_left, height[left - 1]);
int mins = max_left;
if (mins > height[left])
{
sum = sum + (mins - height[left]);
}
left++;
//从右到左更
}
else
{
max_right =max(max_right, height[right + 1]);
int mins = max_right;
if (mins > height[right])
{
sum = sum + (mins - height[right]);
}
right--;
}
}
return sum;
}
};

时间复杂度:O(n)

空间复杂度:O(1)

解法4:栈

说到栈,我们肯定会想到括号匹配了。我们仔细观察蓝色的部分,可以和括号匹配类比下。每次匹配出一对括号(找到对应的一堵墙),就计算这两堵墙中的水。

我们用栈保存每堵墙。

当遍历墙的高度的时候,如果当前高度小于栈顶的墙高度,说明这里会有积水,我们将墙的高度的下标入栈。

如果当前高度大于栈顶的墙的高度,说明之前的积水到这里停下,我们可以计算下有多少积水了。计算完,就把当前的墙继续入栈,作为新的积水的墙。

总体的原则就是,

当前高度小于等于栈顶高度,入栈,指针后移。

当前高度大于栈顶高度,出栈,计算出当前墙和栈顶的墙之间水的多少,然后计算当前的高度和新栈的高度的关系,重复第 2 步。直到当前墙的高度不大于栈顶高度或者栈空,然后把当前墙入栈,指针后移。

作者:windliang
链接:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution {
public:
int trap(vector<int>& height) {
int sum=0;
int n=height.size();
stack<int> st;
int current=0;
while(current<n)
{
while(!st.empty()&&height[current]>height[st.top()])
{
int h=height[st.top()];
st.pop();
if(st.empty())
{
break;
}
int distance=current-st.top()-1;
int mins=min(height[current],height[st.top()]);
sum+=(mins-h)*distance;
}
st.push(current);
current++;
}
return sum;
}
};

时间复杂度:O(n)

空间复杂度:O(n)