这是博主今天的Leetcode 每日一题——接雨水(困难),这道题做了一天也没有做出什么花样来,最后看了题解后,发现居然有那么多种解法,但是自己一个也没有想到,真的是被自己菜哭的一天 ,博主就根据自己的理解,写了这篇题解,希望对其他的小伙伴有所帮助。(插一句题外话,之所以写这篇题解,是因为它的解法很多,个人感觉很有价值,也有助于博主日后的再巩固,提供方便)话不多说,我们来看看,几种解法。(博主个人的理解哦)
首先,暴力解法是一定是超时(自己已经试过了),如果不想看的小伙伴可以自动跳过,但是,博主在这里依然要写这种方法的原因是有的时候,我们可能想不到正解,只能通过题意或者自己的理解写出超时的代码(有的甚至都写不出来),但是这在样的代码上如果有优化,有可能就是正确解!那么就让我们来看看暴力解法的具体实现吧
①:我们枚举数组中的每一个元素,以该元素为分界线,找出其左边的最大值(left_max),再找出其右边的最大值(right_max),然后求left_max和right_max中的最小值。(为什么求最小值,就像木桶原理一样,最短的木板决定了,木桶最终的盛水量,后面的题解中也是这个意思,就不再解释)
②:我们将求得的最小值-该元素的值,即为该处的积水量,然后用ans来累加记录。
class Solution {
public:
int trap(vector& height) {
int n=height.size();
if(n==1)
return 0;
int ans=0;
for(int i=1;i=0;j--)//以该元素为分界线找左边的最大值
{
left_max=max(left_max,height[j]);
}
for(int j=i;j
对于动态编程的思想,博主个人觉得就是对暴力解法的进一步优化,我们都知道暴力解法是求一个元素左边的最大值与右边的最大值,求两者之间的最小值,再与该元素求差,作为该处的积水量,而在动态编程中:
①:我们首先用两个动态数组left_max[],right_max[],分别保存从左到右(和从右到左的),每个元素左边的最大值(右边的最大值)。
②:然后再通过遍历一遍数组,比较每个元素对应的min(left_max[],right_max[])-该处的元素值,就是该处雨水的积水量,最后用ans来累加即可,这样我们就把O(n*n)的时间复杂度将为O(n)了。
③:至于left_max[],right_max[]的求解,博主个人感觉有点求“前缀和”和“后缀和”的感觉,只不过对于left_max[],right_max[]来说,是求比较后的最大值,而不是累加和;
class Solution{
public:
int trap(vector& height){
int n=height.size();
if(n==1)
return 0;
int ans=0;
vectorleft_max(n),right_max(n);//定义好两个动态数组
left_max[0]=height[0];//保存left_max[]的第一个值
for(int i=1;i=0;i--)//一次将right_max[]的每个元素赋值
{
right_max[i]=max(right_max[i+1],height[i]);
}
for(int i=0;i
来啦来啦,双指针它来了,不得不的说双指针的解法真的很妙,怎么一个妙法呢?让我慢慢道来。
首先我们在动态编程的方法中知道,当left_max[i]
right_max left_max __ __ | | | |__ __?????????????????????? | | __| |__| __| |__ left right
因为此时,从左到右的数对该数(left)来说是绝对可信的,而右边的数不一定,同理来说,当左端的数大时,那么积水的高度就依赖于当前方向的高度(从右到左),有了这样的思想我们就可以有一下的操作了:
①:定义left和right分别指向原数组的左右两端的下标;
②:用left_max,right_max分别保存左右两端的最大值;
③:如果,height[left]
然后让我们来看看具体的代码实现吧。
class Solution{//双指针
public:
int trap(vector& height){
int n=height.size();
if(n==1)
return 0;
int left=0,right=n-1;//左右边界
int left_max=0,right_max=0;
int ans=0;
while(leftleft_max)
{
left_max=height[left];//更新left_max;
}
else
{
ans+=left_max-height[left];//计算该处的积水量
}
left++;//向右扫描
}
else//对该数的右边的数进行操作
{
if(height[right]>right_max)
{
right_max=height[right];//更新right_max;
}
else
{
ans+=right_max-height[right];//计算该处的积水量
}
right--;//向左扫描
}
}
return ans;//返回积水量
}
};
这道题考到了好几种方法,我们的解题过程,解题方法也是一步一步的优化,一步一步递进的,博主个人觉得我们在平时的解题的时候也应该这样来做,总的来说这道题对我的收获也是很大的,如果有帮助到正在阅读的小伙伴,希望记得点赞、收藏哦!