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

本文探讨了给定一系列垂直线段,如何找出其中两条作为边界,使得这两条线段与x轴围成的容器能够盛放最多的水。通过对比暴力算法和逼近法,详细解析了逼近法的原理和有效性,最终给出了Python实现代码。

题目

给定 nnn 个非负整数 a1,a2,…,ana_1,a_2,…,a_na1,a2,,an,每个整数对应一个坐标为 (i,ai)(i, a_i)(i,ai) 的点。这样形成了 nnn 条竖直的线段,线段的两个端点分别为 (i,ai)(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)之间的容量最大,容量为 min(8,7)∗7=49min(8, 7) * 7 = 49min(8,7)7=49

思路

1、暴力算法

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

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

2、逼近法

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

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

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

严格的证明:

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

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

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

结论是:rrr 不可能位于 aaabbb 之间。

可以用反证法证明:

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

V(l,b)=(b−l)∗min(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)∗min(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)

lllaaa 左边,距离 bbb 的距离更远,因此有:

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

其中,VVV 表示容量,hhh 表示高度。

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

逼近法的时间复杂度为 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))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值