问题:
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.
官方难度:
Medium
翻译:
给定n个非负整数a1,a2,...,an,每一个数值代表坐标轴上的坐标(i,ai)。
画上n条垂直于横坐标的竖线,用于连接点(i,ai)和(i,0)。找到两条线,与x轴一起形成一个容器,能够容纳最多的水。
注意容器不能倾斜。
方法一:
- 利用一个二次循环,同时维护一个最大面积max。
方法一的解题代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private static int method_1(int[] height) { 2 int max = 0; 3 for (int i = 0; i < height.length - 1; i++) { 4 for (int j = i + 1; j < height.length; j++) { 5 int area = Math.min(height[i], height[j]) * (j - i); 6 if (max < area) { 7 max = area; 8 } 9 } 10 } 11 return max; 12 }
方法二:
- 显然第一种方法的效率是极低的。
- 我们注意到,虽然高是由输入决定的,但是底的值却是可控的。有一个思想,维护一个最大高度maxHeight记录遇到的最大高度,在内循环中自后向前遍历,这样一来,底的值始终在减小。这就意味着只需要考虑高的值,就能决定整个容器的面积。所以在遇到小于maxHeight的情况时,可以直接跳过本次循环,不需要再次计算面积。
方法二的解题代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 private static int method_2(int[] height) { 2 int max = 0; 3 for (int i = 0; i < height.length; i++) { 4 int maxHeight = 0; 5 // 内循环反向遍历 6 for (int j = height.length - 1; j > i; j--) { 7 int h = Math.min(height[i], height[j]); 8 // 因为底越来越小,所以只有高度大于最高高度,才有比较面积的意义 9 if (h > maxHeight) { 10 // 不考虑面积比较结果,高先赋值 11 maxHeight = h; 12 if (h * (j - i) > max) { 13 max = h * (j - i); 14 } 15 } 16 } 17 } 18 return max; 19 }
方法三:
- 方法二仍然是一个时间复杂度为O(n^2)的二次循环,但是还有优化的策略。
- 在一次内循环结束之后,进入下一次内循环,如果height[i]小于上一次的长度,可以直接跳过这次循环。这样一来看似二次循环的问题,可以通过从两侧向中间夹逼,转化为一次循环的问题。
方法三的解题代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int maxArea(int[] height) { 2 if (height == null || height.length < 2) { 3 throw new IllegalArgumentException("Input error"); 4 } 5 // 初始面积 6 int left = 0, right = height.length - 1; 7 int area = (right - left) * Math.min(height[left], height[right]); 8 // 左右侧开始的最长高度 9 int leftMax = height[left]; 10 int rightMax = height[right]; 11 // 从两侧向中间夹逼,即底在不断变小 12 while (left < right) { 13 if (height[left] < height[right]) { 14 left++; 15 // 更小的高没有比较价值 16 if (height[left] <= leftMax) { 17 continue; 18 } else { 19 leftMax = height[left]; 20 } 21 area = Math.max(area, (right - left) * Math.min(height[left], height[right])); 22 } else { 23 right--; 24 if (height[right] <= rightMax) { 25 continue; 26 } else { 27 rightMax = height[right]; 28 } 29 area = Math.max(area, (right - left) * Math.min(height[left], height[right])); 30 } 31 } 32 return area; 33 }
相关链接:
https://leetcode.com/problems/container-with-most-water/
PS:如有不正确或提高效率的方法,欢迎留言,谢谢!