LEETCODE专题
11. Container With Most Water
题目要求:
这里要求我们将数组中的每个元素(下标为i,从1开始)看成一条向量,起点为(i, 0), 终点为(i, a[i]),这样每条向量的长度就是a[i]。然后再找出这些向量中的两条,使它们和x轴形成的容器面积最大。当然不允许倾斜向量。
问题:
- 如何计算容器的面积?
- 如何减少时间复杂度?
思路:
- 计算容器的面积,我们需要明白“木桶效应”——在所有组成木桶的木材中,最短的那根对木桶装的水量起着决定性作用。这里也是一样,我们计算容器的面积,其实也就是计算一个长方形的面积。假设i、j分别是这两条向量的下标且i是a[i] < a[j],那么宽就是两条向量在x轴上的投影点的距离,也就是下标的差;长则是两条向量中最短的那条向量的终点的y值,也就是a[i]。
- 这里笔者曾经使用过两重循环。后来发现这种解法十分的naive,时间复杂度为O(n^2)的算法分分钟超时。于是改进了一下思路,发现,其实这和快速排序算法十分的相似,即刚开始选定第一条向量和最后一条向量,然后让长度较短的一条向量的下标往另一个下标靠拢,若移动的过程中得到的新的向量的长度比之前的短,继续移动,直到得到的向量的长度更长为止,此时记录新的容器面积,得到的倘若比最大面积大的话就覆盖最大面积。这里之所以可以抛弃长度更短的向量,是因为下标在移动的过程中本身宽就在减少,由上面的讨论可知,此时长由该向量长度决定,若得到的长度更短,容器面积自然也更小,也就没有记录新的容器面积的必要了。
下面直接上代码:
class Solution {
public:
int maxArea(vector<int>& height) {
/*
* 1 loop.
* Firstly initial the maxArea to be
* the area of the container formed
* by the first line and the last
* line. Then let index of the shorter
* line goes closer to the longer one.
*
* Record the area each time it finds
* a larger one.
*
*/
int width, length, index;
int maxArea = -1;
int left = 0, right = height.size() - 1;
do {
width = right - left;
if (height[left] < height[right]) {
length = height[left];
index = left;
while (left < right && height[left] <= length) left++;
} else {
length = height[right];
index = right;
while (left < right && height[right] <= length) right--;
}
maxArea = maxArea > width * length ? maxArea : width * length;
} while (left < right);
return maxArea;
}
};
时间复杂度:O(n)