1. 思路A
其实观察可以很容易发现,对于每个柱子最后能到达的水面高度取决于左右两边的最高柱子的最小值。所以只需要左右两边扫描一边,得到左边前i个柱子的最高值和右边前i个的最高值。那么对于某个柱子 j,其水面高度为 : min(left_max[j], right_max[j])。
时间复杂度O(n),空间复杂度O(n)
2.代码A
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
vector<int> left_max(n+2),right_max(n+2);
for(int i = 0; i < n; ++i)
{
left_max[i+1] = max(left_max[i], height[i]);
right_max[n - i] = max(right_max[n - i + 1], height[n - i - 1]);
}
int ans = 0;
for(int i = 0; i < n; ++i)
{
ans += min(left_max[i+1], right_max[i+1]) - height[i];
}
return ans;
}
};
3. 思路B
和思路A很像,不过通过栈去维护,遇到比栈顶柱子高度小的就入栈,遇到比栈顶柱子高的说明就可以产生凹形,从而弹出栈顶并计算可以存储的水。
这里需要注意的就是,因为栈不仅要记录柱子的高度、还需要记录柱子的位置。所以我们可以入栈位置,通过位置可以直接访问数组得到高度。
时间复杂度O(n),空间复杂度O(n)
4.代码B
class Solution {
public:
int trap(vector<int>& height) {
stack<int> stk;
int ans = 0;
for(int i = 0; i < height.size(); ++i)
{
while(!stk.empty() && height[stk.top()] < height[i])
{
int cur = stk.top();
stk.pop();
if(stk.empty())break;
ans += (min(height[stk.top()], height[i]) - height[cur]) * (i - stk.top() - 1);
}
stk.push(i);
}
return ans;
}
};
5. 思路C
双指针,这个思路我是没有想出来。
重新梳理一下思路A中最重要的一句话:
对于每个柱子最后能到达的水面高度取决于左右两边的最高柱子的最小值。
也就是说对于每个柱子在其左右两边都有两个能决定其水平面的高度的柱子,并且是以这两个柱子中的小值决定的。
所以我们初始化两个指针在最左和最右的两个柱子,然后每次移动两个指针指向的柱子矮的那个(因为矮的限制水面),才能保证两个柱子尽可能的是最高的。并且统计左右指针移动中各自最大的柱子高度。那么每次指针指向的柱子决定其水面高度的一定是此时两个指针各自记录的最大高度中的最小值。(有点绕)
6.代码C
class Solution {
public:
int trap(vector<int>& height) {
int l = 0, r = height.size() - 1, left_max = 0, right_max = 0, min_max = 0, ans = 0;
while(l <= r)
{
left_max = left_max > height[l] ? left_max : height[l];
right_max = right_max > height[r] ? right_max : height[r];
min_max = left_max > right_max ? right_max : left_max;
ans += height[l] < height[r] ? min_max - height[l++] : min_max - height[r--];
}
return ans;
}
};