11. 盛最多水的容器

举报
yd_274940399 发表于 2026/11/08 20:00:24 2026/11/08
【摘要】 11. 盛最多水的容器给一个数组 height,height[i] 是一条竖线的高度,两条线和 x 轴围成一个容器,能装多少水由矮的那条线决定:水量 = min(左高,右高) × 两线之间的距离。题目(出处:力扣 11 盛最多水的容器)给的第一个例子是 [1, 8, 6, 2, 5, 4, 8, 3, 7],答案是 49,取下标 1 的 8 和下标 8 的 7,宽 7,高 7。 先写最笨的...

11. 盛最多水的容器

给一个数组 height,height[i] 是一条竖线的高度,两条线和 x 轴围成一个容器,能装多少水由矮的那条线决定:水量 = min(左高,右高) × 两线之间的距离。题目(出处:力扣 11 盛最多水的容器)给的第一个例子是 [1, 8, 6, 2, 5, 4, 8, 3, 7],答案是 49,取下标 1 的 8 和下标 8 的 7,宽 7,高 7。

先写最笨的办法

我一开始写的就是两层循环,把所有左右配对都算一遍,取最大的:

def max_area_bruteforce(height):
    best = 0
    for l in range(len(height)):
        for r in range(l + 1, len(height)):
            best = max(best, min(height[l], height[r]) * (r - l))
    return best

这个写法没什么可挑的,就是慢。n 条线要算 n(n-1)/2 对面积,官方例子 n=9 就要算 36 对,n=1000 就是 499500 对。答案是对的,但每次都从头算起,前面算过的信息一点没用上。我跑了一下,n=1000 的随机数据(高度 1 到 10000,种子 7)上它要枚举 499500 对,而下面这个办法只算 999 次,答案都是 9338922。

面积由矮边说了算

想快一点,得先看清面积到底由什么决定。min(左高,右高) × 宽度,宽度是两条线的距离,高度是矮的那条。也就是说,一对线能装多少水,矮边说了算,高边再高也没用。

这个观察直接给出一个办法:左指针放最左,右指针放最右,先算一次面积,然后把矮的那条往中间挪一步,再算,再挪,直到两根指针碰头。每次只挪矮边,是因为挪高边没有意义,宽度变小了,高度还是矮边那个数,面积只会更小,不可能变大。

但这样跳过很多配对,会不会把最大值漏掉?我一开始也担心这个。要想明白这一点,看固定矮边不动时会发生什么:它和任何一条内侧线配对,宽度都比现在小,高度也不会超过这条矮边,面积一定比现在小。所以这条矮边能配出的最好结果就是当前这一次,挪走它不会丢答案。每次挪走的都是一条已经没有更好配对的线,剩下的配对里最大值一定还在。走完一整趟,最大值一定算到过。

拿官方例子走一遍

数组是 [1, 8, 6, 2, 5, 4, 8, 3, 7],下标从 0 开始:

左 右 面积 当前最大
0(高 1) 8(高 7) 1×8=8 8
1(高 8) 8(高 7) 7×7=49 49
1(高 8) 7(高 3) 3×6=18 49
1(高 8) 6(高 8) 8×5=40 49
1(高 8) 5(高 4) 4×4=16 49
1(高 8) 4(高 5) 5×3=15 49
1(高 8) 3(高 2) 2×2=4 49
1(高 8) 2(高 6) 6×1=6 49

第一步左边的 1 是全数组最矮的,挪走它没什么可惜的。第二步就撞上了答案 49。剩下的配对里左指针一直是下标 1 的 8,右端最高的 8 在下标 6,宽 5,面积 40;宽度更大的那一对右端只有 3,面积 18。所以后面超不过 40,也就超不过 49 了。整个过程只算了 8 次面积。

这里有个小地方:第四步两边都是 8,一样高,挪哪边都行。我的代码里写的是 height[l] < height[r] 时挪左,否则挪右,所以一样高时挪右。挪左也一样,因为两边相等时,无论挪哪边,丢掉的配对都不会比当前这一次更好。理由和上面一样,固定其中一边,另一边的内侧配对宽度更小、高度不超过 8。两种写法答案相同,我实测过,官方例子两种走法都是 49。

正式代码

def max_area(height):
    l, r = 0, len(height) - 1
    best = 0
    while l < r:
        # 当前这对线的面积,矮边决定高度
        area = min(height[l], height[r]) * (r - l)
        best = max(best, area)
        # 挪矮边:只有它动,面积才可能变大
        if height[l] < height[r]:
            l += 1
        else:
            r -= 1
    return best

时间复杂度 O(n),每条线最多挪一次;额外空间 O(1),只用了几个变量。

我拿什么验的

官方的两个例子都过了:[1, 8, 6, 2, 5, 4, 8, 3, 7] 得 49,[1, 1] 得 1。然后我写了 3000 组随机数据(先 random.seed(20261108),长度 randint(2, 30),高度 randint(0, 100)),把双指针的结果和暴力枚举逐个对,失配是 0 组。n=1000 那组也是同样的做法,random.seed(7),高度 randint(1, 10000)。边界也都顺手测了:[1, 2] 和 [2, 1] 都是 1;[5, 5, 5, 5] 是 15,取两头,宽 3 高 5;递增的 [1, 2, 3, 4, 5] 是 6,取 2 和 5,宽 3 高 2;递减的 [5, 4, 3, 2, 1] 也是 6;带 0 的 [0, 0] 是 0,[0, 5, 0] 也是 0,因为矮边是 0,水量就是 0。

回头看,代码只有十来行,难处在敢不敢跳过那些没算过的配对。暴力是把所有可能都算一遍,求个安心;双指针是先想明白矮边已经没有更好的配对,才敢跳过。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。