问题描述:Given n non-negative integers a1, a2, ..., an, where each represents a pointat coordinate (i, ai). n vertical lines aredrawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find twolines, which together with x-axis forms a container, such that the containercontains the most water.
Note: You may not slant the container.
问题分析:水桶问题,取决于|i – j|* min(a[i] ~ a[j]),即a[i]到a[j]的最小值乘以距离
代码:
最优解法:类似于TwoSum:http://blog.csdn.net/woliuyunyicai/article/details/44222949
直观的解释是:容积即面积,它受长和高的影响,当长度减小时候,高必须增长才有可能提升面积,所以我们从长度最长时开始递减,然后寻找更高的线来更新候补;
故根据贪心算法,应使i之前的数都比a[i]要小,j之后的数都比a[j]要小;
所以我们从两头向中间靠拢,同时更新候选值;在收缩区间的时候优先从 x, y中较小的边开始收缩。
类似于2Sum的思想,两边设一个指针,然后计算area,如果height[i] <= height[j],那么i++,因为在这里height[i]是瓶颈,j往里移只会减少面积,不会再增加area。
这是一个贪心的策略,每次取两边围栏最矮的一个推进,希望获取更多的水。
一个不严格的证明:
当height[i] <=height[j]时,为什么是i++,而不是j++来获取可能更多的水?
假设j' > j,之所以j'往左移,是因为存在height[i'] > height[j'] (i’ <= i), 而那时area' = (j' - i') *min(height[i'], height[j']),
因为height[j'] ==min(height[i'], height[j']),所以area' = (j' - i') *height[j']。而i 和 j'构成的面积area = (j' - i) * min(height[i], height[j'])。area' >= area,所以j不需要往右移。
代码:
public int maxArea(int[] height) {
if(height == null || height.length == 0)
{
return 0;
}
int i = 0;
int j = height.length() - 1;
int ret = 0;
while(i < j)
{
int area = (j - i) * Math.min(height[i], height[j]);
ret = ret > area ? ret : area;
if (height[i] <= height[j])
i++;
else
j--;
}
return ret;
}
原始解法,时间复杂度O(O^3)
public class Solution {
public int maxArea(int[] height) {
if(height == null || height.length == 0)
{
return 0;
}
int max = 0;
for(int j = 1; j < height.length; j++)
{
int temp_max = 0;
for(int i = 0; j + i < height.length; i++)
{
int temp = MinValue(height, i , j + i);
temp_max = temp > temp_max ? temp : temp_max;
}
max = (temp_max * j) > max ? (temp_max * j) : max;
}
return max;
}
//比较数组i-j的最小值
private int MinValue(int[] data, int start , int end)
{
int min = Integer.MAX_VALUE;
for(int i = start; i <= end; i++)
{
if(min > data[i])
{
min = data[i];
}
}
return min;
}
}
解法二:改进算法二,时间复杂度O(n^2),空间复杂度O(n)
即使用一个数组,存储上一次循环[i , j -1]的最小值saved_min,此次循环中计算[i,j]最小值,仅需要比较saved_min[i]与height[j]比较得出最小值即可;
public class Solution {
public int maxArea(int[] height) {
if(height == null || height.length == 0)
{
return 0;
}
int max = 0;
int[] saved_min = new int[height.length];
for(int j = 1; j < height.length; j++)
{
int temp_max = 0;
for(int i = 0; j + i < height.length; i++)
{
if(j == 1)
{
saved_min[i] = MinValue2(height[i],height[i + j]);
}
else
{
saved_min[i] = MinValue2(saved_min[i],height[i + j]);
}
temp_max = saved_min[i] > temp_max ? saved_min[i] : temp_max;
}
max = (temp_max * j) > max ? (temp_max * j) : max;
}
return max;
}
//比较数组i-j的最小值
private int MinValue2(int a , int b)
{
return a < b ? a : b;
}
}