(双指针 | 思维)11. 盛最多水的容器

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
 

整体思路:将每条边作为短边,寻找另一条距离它最远的长边(长边>=短边)

题解1:双指针方法,参考了leetcode的想法,确实比较简洁,也符合我的思路;

// 16ms 左右,时间复杂度:O(n)
class Solution {
public:
    

    int maxArea(vector<int>& a) {

        int ans=0;
        int l=0,r=a.size()-1;
        while(r>l){
            ans=max(ans,min(a[l],a[r])*(r-l));
            if(a[l]<=a[r]) l++;
            else r--;
        }
        return ans;
    }
};

题解2:用线段树维护,第一思路就是这个了,写起来比较麻烦;

             范围变成了 1 …… n ,我将 vector<>a,整体向后移了一位,因为我写线段树习惯将根设为 1。

             对 a 中每个位置值 x 要进行两次线段树查询,第一次查询在其左边且最靠前 >= x 的位置;

                                                                                      第二次查询在其右边且最靠后 >= x 的位置;

// 56ms左右,时间复杂度:O(n*logn)
class Solution {
public:
    int tree[40005],index=-1;       //  tree[]:存放树,index代表每次查出来的位置

    void sett(int l,int r,int rt,vector<int>& a){   // 建树,这里线段树维护的是每个区间最大
        if(l==r){                                   // 值的下标位置
            tree[rt]=l;
            return ;
        }
        int mid=l+r>>1;
        sett(l,mid,rt<<1,a);
        sett(mid+1,r,rt<<1|1,a);
        if(a[tree[rt<<1]]>a[tree[rt<<1|1]]) tree[rt]=tree[rt<<1];
        else tree[rt]=tree[rt<<1|1];
    }

    //  flag 为 false 代表左查找;flag 为 ture 代表右查找
    void findd(int x,int px,int py,int l,int r,int rt,vector<int>& a,bool flag){
        if(index!=-1) return ;                // 找到后,返回
        if(l==r){                             // 找到底部
            if(px<=l&&l<=py) index=l;         // 判断找到的是否在符合的区间范围
            return ;
        }
        int mid=l+r>>1;
        if(!flag){
            // 左查找:代表尽量向左边查找,先查左再查右
            if(a[tree[rt<<1]]>=a[x]) findd(x,px,py,l,mid,rt<<1,a,flag);
            if(a[tree[rt<<1|1]]>=a[x]) findd(x,px,py,mid+1,r,rt<<1|1,a,flag);
        }
        else{
            // 右查找:代表尽量向右边查找,先查右再查左
            if(a[tree[rt<<1|1]]>=a[x]) findd(x,px,py,mid+1,r,rt<<1|1,a,flag);
            if(a[tree[rt<<1]]>=a[x]) findd(x,px,py,l,mid,rt<<1,a,flag);
        }
    }

    int maxArea(vector<int>& a) {
        a.push_back(0);
        for(int i=a.size()-1;i>=0;i--) a[i+1]=a[i];    //  范围向后移一格

        sett(1,a.size(),1,a);                          //  建树

        int ans=0;
        for(int i=1;i<a.size();i++){
            index=-1;
            findd(i,1,i,1,a.size(),1,a,false);         //  左查
            int l=index;
            index=-1;
            findd(i,i,a.size(),1,a.size(),1,a,true);   //  右查
            int r=index;
            ans=max(ans,(r-i)*a[i]);
            ans=max(ans,(i-l)*a[i]);
        }
        return ans;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值