Leetcode算法——11、容量最大的容器

76 篇文章 1 订阅

题目

给定 n n n 个非负整数 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,,an,每个整数对应一个坐标为 ( i , a i ) (i, a_i) (i,ai) 的点。这样形成了 n n n 条竖直的线段,线段的两个端点分别为 ( i , a i ) (i, a_i) (i,ai) ( i , 0 ) (i, 0) (i,0)

要求找到两条线段当做两个挡板,使得他们与 x 轴组成的容器可以盛最多的水,并输出最大容量。

示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
解释: 在第2根线(8)与最后一根线(7)之间的容量最大,容量为 m i n ( 8 , 7 ) ∗ 7 = 49 min(8, 7) * 7 = 49 min(8,7)7=49

思路

1、暴力算法

最容易想到的算法为暴力算法,即编写一个双重循环,遍历所有不同两根线段,计算线段间的容量,选出最大容量。

这时候算法复杂度为 O ( n 2 ) O(n^2) O(n2)

2、逼近法

定义两个指针,分别指向最左边的线段和最右边的线段,然后逐渐将两个指针往中间移动,每次移动后计算一边当前的容量,然后选择其中最大的容量。但是这样做不会遍历所有不同的两根线段,如何保证一定会遍历到最大容量的两根线段呢?

策略是,每次移动时,将左右指针的两根线段中的较短的线段,往中间移动一位。

为什么要这样做?因为容器的容量,是由两根线段中较短的那根决定的,改变短线段可以改变容量。

严格的证明:

在这里插入图片描述
图示是很多线段中的一部分,不失一般性地,比如某一次迭代之后,左右指针已经移动到了 l l l r r r 的位置(即蓝色轮廓),下一步该选择是 l l l 向右移动一位,还是 r r r 向左移动一位。

假如我们最后的答案是 a a a b b b 之间的容量是最大的(即红色线段),其中 b b b r r r 是同一个线段。如果 l l l 能一直向右移动,直至和 a a a 重合,那么说明算法成功,找到了最大容量的 a b ab ab 线段。

那么有没有可能出现 r r r 向左移动一位,导致 r r r 位于 a a a b b b 之间呢?如果这样,那么之后无论再怎么移动,都不可能找到最优解 a b ab ab 了,算法也就失败了。

结论是: r r r 不可能位于 a a a b b b 之间。

可以用反证法证明:

假如下一步是 r r r 向左移动一位,那么根据移动策略,现在必然有 l l l 的高度大于 r r r 的高度,才会使得 r r r 左移。这样,一定会有 l l l b b b 组成的容量大于 a a a b b b 组成的容量,因为:

V ( l , b ) = ( b − l ) ∗ m i n ( l , b ) = ( b − l ) ∗ h ( b ) V(l,b)=(b-l)*min(l,b)=(b-l)*h(b) V(l,b)=(bl)min(l,b)=(bl)h(b)
V ( a , b ) = ( b − a ) ∗ m i n ( a , b ) ≤ ( b − a ) ∗ h ( b ) V(a,b)=(b-a)*min(a,b)\le (b-a)*h(b) V(a,b)=(ba)min(a,b)(ba)h(b)

l l l a a a 左边,距离 b b b 的距离更远,因此有:

V ( a , b ) &lt; V ( l , b ) V(a,b)&lt;V(l,b) V(a,b)<V(l,b)

其中, V V V 表示容量, h h h 表示高度。

这和 a b ab ab 是最大容量相矛盾。因此,逼近法是有效的,肯定会找到正确答案。

逼近法的时间复杂度为 O ( n ) O(n) O(n)

python实现

def maxArea(height):
    """
    :type height: List[int]
    :rtype: int
    从两边往中间寻找。
    """

    max_area = 0
    l = 0
    r = len(height) - 1
    while(l < r):
        max_area = max(max_area, min(height[l], height[r]) * (r - l))
        if height[l] < height[r]:
            l += 1
        else:
            r -= 1
    return max_area

if '__main__' == __name__:
    height = [1,8,6,2,5,4,8,3,7]
    print(maxArea(height))
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值