Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container.
» Solve this problem
1.最简单的算法:
枚举所有可能的情况,求出最大面积。
时间复杂度O(n2)
2.排序:
在方法1中,我们其实做了很多不必要的工作。
例如:枚举了(2,8),(2,7),(2,5),(2,4),(2,3),因为2要低于3,4,5,7,8,因此这五组的高度都是由2的高度决定。
即,area(2,8)=height(2)*(8-2)>area(2,7)=height(2)*(7-2)> ...>area(2,3)=height(2)*(3-2)。
所以,我们只用考虑离2最远的8即可。
算法的基本思想就是对所有的高度由低到高排序,然后依次取出,每次找寻离该点最远的一根。
for (int i = 0; i < sortedHeight.size(); i++) {
max = max{sortedHeight[i] * (i - left), sortedHeight[i] * (right - i), max}
}
维护left和right(left指的是现在剩余的线中最左边的,right指的是现在剩余的线中最右边的):
取出i之后,需要更新left和right。
可以使用一个标记数组,记录哪些线是已经取出的。如果left已经取出则不断向左移,同法维护right。
for (int i = 0; i < sortedHeight.size() - 1; i++) {
max = max{sortedHeight[i] * (i - left), sortedHeight[i] * (right - i), max};
used[sortedHeight[i].在原来的height数组中的下标] = true;
while (used[left]) {
left++;
}
while (used[right]) {
right--;
}
}
时间复杂度为O(nlogn)。
class Component {
public:
int height;
int no;
Component() {}
Component(int h, int n): height(h), no(n) {}
};
bool comp(const Component &c1, const Component &c2) {
return c1.height < c2.height;
}
// nlogn
class Solution {
public:
int maxArea(vector<int> &height) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int count = height.size();
Component components[count];
for (int i = 0; i < count; i++) {
components[i] = Component(height[i], i);
}
sort(components, components + count, comp);
bool hash[count];
memset(hash, 0, sizeof(bool) * count);
int right= count - 1, left= 0, max = 0;
int temp1 = 0, temp2 = 0;
for (int i = 0; i < count - 1; i++) {
temp1 = (right - components[i].no) * components[i].height;
temp2 = (components[i].no - left) * components[i].height;
max = temp1 > max ? temp1 : max;
max = temp2 > max ? temp2 : max;
hash[components[i].no] = true;
while (hash[right]) {
right--;
}
while (hash[left]) {
left++;
}
}
return max;
}
};
3.最快速的算法:
再次看看这个图。
我们看看(1,8)和(1,7):
由于height[1]<height[8],所以area(1,8)=height[1]*(8-1)。而height[7]>height[8]>height[1],所以area(1,7)=height[1]*(7-1)<area(1,8)。
假设,height[7]<=height[8],area(1,7)和area(1,8)的关系会是什么。
1.height[1]<height[7]<=height[8]:area(1,7)=height[1]*(7-1)<area(1,8);
2.height[7]<=height[1]<height[8]:area(1,7)=height[7]*(7-1)<=height[1]*(7-1)<area(1,8)。
所以,无论如何area(1,8)>area(1,7)。
同理可知,area(1,8)>area(1,7)>area(1,6)>area(1,5)>...>area(1,2)。
因此,我们只需要计算area(1,8)。
下一步,我们需要考虑的是area(2,8),因为height[2]<height[8],所以area(2,8)>area(2,7)>area(2,6)>...>area(2,3)。
当我们计算area(4,8)时,因为height[4]>height[8],类似地,可以推出area(4,8)>area(5,8)>area(6,8)>area(7,8)。
之后,我们计算的将是(5,7)和(6,7)。
算法的雏形:
while (head < tail) {
if (height[head] < height[tail]) {
max = max{height[head] * (tail - head), max};
head++;
}
else {
max = max{height[tail] * (tail - head), max};
tail--;
}
}
代码:
class Solution{
public:
int maxArea(vector<int> &height) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int head = 0, tail = height.size() - 1;
int max = 0, temp = 0;
while (head < tail) {
temp = tail - head;
if (height[head] < height[tail]) {
temp *= height[head];
head++;
}
else {
temp *= height[tail];
tail--;
}
if (temp > max) {
max = temp;
}
}
return max;
}
};