小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原题:11. 盛最多水的容器
简而言之,就是在下图中找到两根柱子,它们能够组成一个容量最大的容器。这些柱子中相邻柱子的距离都是相等的,柱子的高度以一个正整数数组作为参数提供。
解题思路:
最容易想到的方法就是两层循环,计算所有柱子组合可以组成的容器的容积,找其中的最大值。结社左边柱子的高度是 leftHeight
,右边柱子的高度是 rightHeight
,它们的组成的容器宽度是 width
,那么容积的计算方式是:min(leftHeight, rightHeight) * width
。
但是 O(n^2)
的复杂度肯定不是我们想要的答案,因此,可以再进一步进行分析。
在一个数组选定一定范围或者一个起始点的算法题,通常可以用双指针方法来完成。也就是定义两个指针,指向两个起始的元素,通过一定的规则移动数组,在此过程中找到想要的答案。这种方法通常可以以 O(n)
的复杂度解决问题。
这道题可以通过以下的方法解决:
定义左右两个指针 l
和 r
,开始时他们分别指向最左的最右的元素,计算两个柱子可以组成的容器的容积。然后,通过下面的方法移动指针。
- 如果左边柱子的高度更高,则右指针向左移动一个元素
- 如果右边柱子的高度更高,则左指针向右移动一个元素
- 如果它们两个一样高,则移动任意一个指针都可以
每次移动一个指针之后,都计算当前两个指针指向的元素组成的容器的容积,知道左右指针相遇,取过程中计算到最大的容积,既是最终的答案。这样,就在 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;
}
}