这道题一共有两种思路,四个解法
思路一:
一列一列计算,对于每一列我们都计算一下上面可以放多少水,基本思路就是找一下左边最高的柱子,找一下右边最高的柱子,取最小值减去这一列柱子高度,即为在这一列上可以放多少水。
解法一:
两次dp遍历,一次找左边的最大值,一次找右边的最大值
int trap(vector<int>& height) {
int n=height.size();
vector<int> dp(n,0);
int mn=0;
int i;
for(i=0;i<n;i++)
{
dp[i]=mn;
mn=max(mn,height[i]);
}
int ans=0;
mn=0;
int edge;
for(i=n-1;i>=0;i--)
{
edge=min(mn,dp[i]);
if(edge>height[i]) ans+=edge-height[i];
mn=max(mn,height[i]);
}
return ans;
}
解法二:
只需要一次遍历即可。需要两个指针一个从左向右,一个从右向左,选取指向柱子中较小的那一个并从那一边从另一边扫描,选取比它小的柱子,计算这些柱子上面可以存储的水。因为是从两边开始扫描的,所以能确定一边肯定是最大值,另一边比最大值大即可,我们并不关心另一边的最大值时多少,反正都是要在两个最大值中取一个较小值的。
int trap(vector<int>& height){
int n=height.size();
int i=0,j=n-1;
int ans=0;
int mn;
while(i<j)
{
mn=min(height[i],height[j]);
if(mn==height[i])
{
i++;
while(height[i]<mn&&i<j)
{
ans+=mn-height[i];
i++;
}
}
else
{
j--;
while(height[j]<mn&&i<j)
{
ans+=mn-height[j];
j--;
}
}
}
return ans;
}
解法三:
思路和解法二一样,但是写起来更简洁一些
欣赏即可,我不习惯这么写
int trap(vector<int>& height) {
int l = 0, r = height.size() - 1, level = 0, res = 0;
while (l < r) {
int lower = height[(height[l] < height[r]) ? l++ : r--];
level = max(level, lower);
res += level - lower;
}
return res;
}
思路二:
用栈的思想来做,这个我是无论如何也想不出来的。
这个思路不再是一列一列算,而是按行算。
依次把每一列和栈顶元素比较,如果对应列小于栈顶坐标对应列或栈为空,无法积水,压入栈。如果大于,则弹出栈顶元素,如果此时为空,说明是边界,无法积水,否则可以积水。但是这里注意,不仅仅是两根柱子的高度差,还需要乘以他们间的距离。
举个例子,对于3,2,1,2,3。我们先计算了2,1,2中间面积为1的坑,然后是把1看成2,计算了3,2,2,2,3中面积为3的坑。
解法四:
这个解法里面有很多的注意点。首先一个i-s.top()-1中的1不要忘记减去,还有一个是这里用while循环而不用for循环是因为它需要循环判断,一是它不一定只做一个底的边,如4,1,2,3;3会和2,1都进行相同操作。而是边也有可能做底,如4,2,3,4;3做了2的边以后入栈,做了4的底。
int trap(vector<int>& height){
stack<int> s;
int n=height.size();
int i=0;
int ans=0;
while(i<n)
{
if(s.empty()||height[s.top()]>=height[i])
s.push(i++);
else
{
int p=s.top();
s.pop();
if(!s.empty()) ans+=(min(height[i],height[s.top()])-height[p])*(i-s.top()-1);
}
}
return ans;
}