盛水最多的容器、接雨水

接雨水 

0cb5d6482e93459cab9e4998d0dbbf5f.png

这道题我们怎么做呢?

假设每个位置都有宽度为1的桶,计算接水多少需要知道左边木板的高度和右边木板的高度,这两个取最小值,左边木板的高度取决于左边的最大高度(高流低不流),右边木板的高度取决于右边的最大高度,那么具体应该怎么实现呢?

方案一:前后缀分解

使用两个数组,第一个数组存储最左边到第i个位置的最大高度(前缀最大值:上一个最大值和高度取最大值)

第二个数组存储从最右边到第i个位置的最大高度(后缀最大值)

最后我们分别遍历前缀最大值和后缀最大值和当前高度,使用后缀最大值和前缀最大值的差减去当前高度即为可接水的高度,把每个水桶能接的水算出来,相加即为答案

时间复杂度:O(N)

int trap(int* height, int heightSize) 
{
    int n=0;
    int pre[20000]={0};
    int last[20000]={0};
    pre[0]=height[0];
    for(int i=1;i<heightSize;i++)
    {
        pre[i]=fmax(height[i],pre[i-1]);
    }
    last[heightSize-1]=height[heightSize-1];
    for(int i=heightSize-2;i>=0;i--)
    {
        last[i]=fmax(height[i],last[i+1]);
    }
    for(int i=0;i<heightSize;i++)
    {
        n+=fmin(pre[i],last[i])-height[i];
    }
    return n;
}

方案二:相向双指针

第一种方法在时间上已是最优,那么能不能在空间上进行优化呢?

假设我们已经计算出了一部分前缀的最大值和一部分后缀的最大值,如果前缀最大值比后缀最大值小,那么左边这个木桶的容量就是前缀最大值,算完之后将其向右扩展;反之,若后缀最大值小于前缀最大值,那么右面木桶的容量就是后缀最大值,算完之后将其向左扩展。

怎么用代码实现呢?

用两个指针

不需要创建额外数组,空间复杂度为O(1)

代码:

int trap(int* height, int heightSize) 
{
    int n=0;
    int left=0;
    int right=heightSize-1;
    int pre_max=0;    //前缀最大值
    int suf_max=0;    //后缀最大值
    while(left<=right)
    {
        pre_max=fmax(pre_max,height[left]);
        suf_max=fmax(suf_max,height[right]);
        if(pre_max<suf_max)
        {
            n+=pre_max-height[left];
            left++;
        }
        else
        {
            n+=suf_max-height[right];
            right--;
        }
    }
    return n;
}

盛水最多的容器

543300796e62435c86ce8e633f4a1329.png

 这道题给了我n条线,我需要从中选择两条构成一个容器,容器的高度取决于短线,容器的宽度取决于这两线的距离(下标差),我们随便选择两条线,容纳的水即为图中蓝色面积,短的线和中间的线构成容器,则有两种情况:

1.中间的线比它短:容纳的水宽度、高度都变少,面积肯定也变少

2.中间的线比它长(或一样长):容纳的水宽度变少,高度不变,面积也变少

所以得出结论:中间的任何线都无法和它构成一个容量更大的容器,即若要找到比蓝色区域更大的容器,则肯定不包含那条线(直接去掉、在剩下的线中继续找)

做法:初始化两个指针:

l=0;
r=n-1;

哪条短就移动哪条,若两条线一样长,则移动哪个都可以 ,移动前先算出这两条线的面积,如果比答案大就更新答案

时间复杂度:O(N)

实现代码:

int maxArea(int* height, int heightSize) 
{
    int left=0;
    int n=0;
    int right=heightSize-1;
    while(left<right)
    {
        int area=(right-left)*fmin(height[left],height[right]);
        n=fmax(n,area);
        height[left]<height[right]?left++:right--;   
    }
    return n;
}

 

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值