LeetCode - 11. 盛最多水的容器

272 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。


原题:11. 盛最多水的容器

简而言之,就是在下图中找到两根柱子,它们能够组成一个容量最大的容器。这些柱子中相邻柱子的距离都是相等的,柱子的高度以一个正整数数组作为参数提供。

image.png

解题思路:

最容易想到的方法就是两层循环,计算所有柱子组合可以组成的容器的容积,找其中的最大值。结社左边柱子的高度是 leftHeight,右边柱子的高度是 rightHeight,它们的组成的容器宽度是 width,那么容积的计算方式是:min(leftHeight, rightHeight) * width

但是 O(n^2) 的复杂度肯定不是我们想要的答案,因此,可以再进一步进行分析。

在一个数组选定一定范围或者一个起始点的算法题,通常可以用双指针方法来完成。也就是定义两个指针,指向两个起始的元素,通过一定的规则移动数组,在此过程中找到想要的答案。这种方法通常可以以 O(n) 的复杂度解决问题。

这道题可以通过以下的方法解决:

定义左右两个指针 lr,开始时他们分别指向最左的最右的元素,计算两个柱子可以组成的容器的容积。然后,通过下面的方法移动指针。

  • 如果左边柱子的高度更高,则右指针向左移动一个元素
  • 如果右边柱子的高度更高,则左指针向右移动一个元素
  • 如果它们两个一样高,则移动任意一个指针都可以

每次移动一个指针之后,都计算当前两个指针指向的元素组成的容器的容积,知道左右指针相遇,取过程中计算到最大的容积,既是最终的答案。这样,就在 o(n) 的复杂度内把问题解决了。

为什么要移动高度较低的一侧的指针就可以了呢?假设左边的高度是 x,右边的高度是 y,它们组成容器的宽度是 w,如果 x 更小(两者相等的情况与此类似)那么,它们组成的容器的容积就是 x * w,如果移动右边的指针,因为指针移动过程中,容器宽度是变小的,而且容器的高度永远都是两个元素中较小的一个,所以,如果移动右边的指针,得到的容器容积不可能比 x * w 更大。因此只需每次移动较小元素的那边的指针,当两遍指针相遇的时候,所有的元素都被遍历了一次,过程中得到的最大容积就是最终的结果。

最终代码:

class Solution {
    public int maxArea(int[] height) {
        int max = 0;
        int l = 0, r = height.length - 1;
        while (l < r) {
            max = Math.max(max, Math.min(height[l], height[r]) * (r - l));
            if (height[l] < height[r]) {
                l++;
            } else {
                r--;
            }
        }
        return max;
    }
}