第一次写博客,我这种水平的大学生程序猿为什么会想到要写博客这种东西呢,而且还要周更呢?没错,这是我们老师布置的作业。如果觉得我的博客写得有何不妥,请在下方留言,每周我会抽取幸运观众进行回复,谢谢大家。
进入正题,这是我们算法老师布置的作业,每周我将在LeetCode上抽取幸运题进行解答,并编写题解在博客上。打开LeetCode,因为我之前尝试过一道难度为中等的题,觉得还行,所以这一次我直接找了一道难度为hard的题,也就是这次我要讲解的题Trapping Rain Water 题目如下
大概意思就是给一个向量,告诉柱形图中每一个黑色的四边形的高度,然后向整个图从上到下注水,看能够盛多少水,一个正方形代表体积为1。英语比较好的同学请自行翻译。这道题我一看就想到了解决方法,那就是一层一层的暴力加。我们依靠上图的例题来解释一下。首先我们看第一层,从第一个小黑块开始,记录一下横坐标,一直到找到下一个小黑块,这两个小黑块中间的空隙长度水占的体积了,一直循环到向量的尾部,然后加第二层,第三层。写完后,加上测试代码,输入题目中的样例,输出正确。心想这种题的难道也能是hard,LeetCode不行啊。兴高采烈的提交后发现竟然超时了,亏我还故意优化了一下时间花费。当场懵逼。附上代码。
class Solution {
public:
int trap(vector<int>& height) {
int ans=0;
int p=-1,q=-1;//记录横坐标
for(int i=1;i<50000;i++)
{
p=-1,q=-1;
for(int j=0;j<height.size() ;j++)
{
if(height[j]>=i)
{
p=j;//将出现的第一个小黑块记录下来
if(q>=0)//如果这不是此行出现的第一个小黑块
{
ans+=p-q-1;//统计
q=p;//更新起点
}
else
q=p;
}
}
if(q<0)//如果一层只有一个或没有小黑块则结束
break;
}
return ans;
}
};
大概想了想时间复杂度,发现我的时间复杂度为O(nm)(m为向量中倒数第二大的数)。好吧,我的思路的确是有问题,如果说向量长度只有三,但是数都是十万多的话问题就很严重了,是十分不划算的。说实话这个时候我也懵了,不知所措。然后就只好转变方向,之前的方法是横向切割,一层一层计算,那么纵向切割呢,这样的话复杂度就是O(n)了,应该可以达到要求,那么问题来了,怎么统计水的体积。
一个很简单的模型,如果两端都有高度为a的黑方条,那么这两个黑方条之间的空间是可以注满水的,现在我们向里面的这个空间填一些小方块,填一个小方块的话在其竖直方向的水就会少一体积,如果说我放的小方块过多而超出了水面呢?设这一很长的条横坐标为t,高度为b,考虑t的右边部分,这一部分的右端高度为a,左端高度为b,根据木桶原理在这两端之间的部分高度为a是可以全部装水的。那么在此区间内再次出现一个高度为c的长条呢?b>c>a那么b和c这一段又是刚才的模型了,利用计算机最擅长的迭代就可以解决啦。
int trap(vector<int>& height) {
int ans=0;
int l=0;//左端点
int r=height.size()-1;//右端点
int m=0;//记录当前最矮的小黑条
while(r>l)
{
if(height[l]<=m)
{
ans+=m-height[l];
l++;
}
else if(height[r]<=m)
{
ans+=m-height[r];
r--;
}
else
m=min(height[l],height[r]);
}
return ans;
}
从两端开始,记录一个当前最矮的,也就是木桶原理中的短板,如果说靠近端点的小黑条的高度低于或齐平,就可以进行一次统计并更新端点,所以判断条件中的等于很重要,要不然是不会更新端点的。循环就有可能陷入死循环。如果在内部出现了一个更高的小黑条区间的话我们就重新记录短板,并在此区间继续上述操作。直到区间长度为0结束,这时我们的统计也完成了。---------------------------------------------手动分割线-----------------------------------------
see you next illusion