力扣42. 接雨水
题目来源(力扣):
https://leetcode.cn/problems/trapping-rain-water/description/
题目描述:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
基本思路1
最直观的方法,对于第k根柱子,扫描它左侧的最高柱子的高度lmax,和右侧的最高柱子的高度rmax,
然后取lmax、rmax中的较小值min(lmax,rmax),再用height[h]-min(lmax,rmax),就能得到当前柱子能储蓄的水量,(如果大于0就)累加进答案ans
代码如下
代码实现1
class Solution
{
public:
int trap(vector<int> &height)
{
int lmax, rmax, minl_r;
int ans = 0;
for (int k = 0; k < height.size(); k++)
{
lmax = height[k];
rmax = height[k];
for (int i = 0; i < k; i++) // 左侧最高的柱子
if (height[i] > lmax)
lmax = height[i];
for (int i = k; i < height.size(); i++) // 右侧最高的柱子
if (height[i] > rmax)
rmax = height[i];
minl_r = min(lmax, rmax); // 取2根柱子中较低的那一根
if (minl_r > height[k])
ans += minl_r - height[k];
}
return ans;
}
};
时间复杂度O(n^2)
时间主要花费在查找lmax和rmax上
时间复杂度
O(n^2)
基本思路2
基本上和思路1一致,注意到在查找lmax和rmax的时候,我们使用的方式是简单的遍历,
如果能够通过预处理,在O(1)时间内求出第k根柱子的左、右侧最高的柱子高度。
该预处理实际上是一个简单的动态规划,即lmax[i]=max(height[i],lmax[i-1]);
只需要预先得到lmax[]即可(rmax[]同理)即可
代码如下
代码实现2
class Solution
{
public:
int trap(vector<int> &height)
{
int len = height.size();
vector<int> lmax(len, 0), rmax(len, 0);
int minl_r, ans = 0;
lmax[0] = height[0];
for (int i = 1; i < len; i++)
lmax[i] = max(height[i], lmax[i - 1]);
rmax[len - 1] = height[len - 1];
for (int i = len - 2; i >= 0; i--)
rmax[i] = max(height[i], rmax[i + 1]);
for (int k = 0; k < len; k++)
{
minl_r = min(lmax[k], rmax[k]); // 取2根柱子中较低的那一根
if (minl_r > height[k])
ans += minl_r - height[k];
}
return ans;
}
};
时间复杂度O(n)
其中预处理时间复杂度O(2*n),得到答案时间复杂度O(n)
所以总时间复杂度O(n)
基本思路3
思路1(思路2)是按列求得每一列的雨水量,最后累加得到ans。
实际上也可以按行求得雨水面积,看起来就像是求图中一个个小坑积累的雨水面积,这就要使用另外一种方式来求解,
利用到的算法容器为单调栈。
需要思考柱子什么时候可以形成小坑。
当柱子单调递减(或单调递增)时,不会形成小坑,不会储水,例如:
[9,7,3]
接下来,遇见一根较高的柱子,就会形成小坑
[9,7,3,6]
由此可见,形成小坑的数据是先减后增的。
而之所以按行求解雨水量,是因为遍历到第k跟柱子时,我们不知道k+1以及之后的柱子的情况,可能使得当前的小坑储水量变大或者形成新的小坑,
因此只需要先管好已经形成的小坑的水量就好了,按行求解会十分方便也符合逻辑。
单调栈的作用就是用于记录高柱子的位置,即小坑的边界,便于在发现小坑时求解储水量。
代码如下
代码实现3
class Solution
{
public:
int trap(vector<int> &height)
{
stack<int> sta; // 记录下标
int ans = 0;
sta.push(0);
for (int i = 1; i < height.size(); i++)
{
if (height[sta.top()] > height[i])
sta.push(i);
else if (height[sta.top()] == height[i])
{
sta.pop();
sta.push(i);
}
else
{ //(可能)形成了小坑
int l, r = i, wei;
// 计算雨水累计值
while (sta.size() > 1 && height[sta.top()] < height[i])
{ // 只有栈中元素数量大于2时才形成小坑
wei = sta.top();
sta.pop();
l = sta.top();
ans += (r - l - 1) * (min(height[l], height[r]) - height[wei]);
}
// 维护单调栈
while (sta.size() && height[i] >= height[sta.top()])
sta.pop();
sta.push(i);
}
}
return ans;
}
};
时间复杂度O(n)
注意每个元素至多入栈和出栈一次
与单调队列的题目比较类似 https://www.cnblogs.com/hb-computer/articles/18519501
浙公网安备 33010602011771号