No.011:Container With Most Water

问题:

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轴一起形成一个容器,能够容纳最多的水。

注意容器不能倾斜。

 

方法一:

  1. 利用一个二次循环,同时维护一个最大面积max。

 

方法一的解题代码:

 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     }
method_1

 

方法二:

  1. 显然第一种方法的效率是极低的。
  2. 我们注意到,虽然高是由输入决定的,但是底的值却是可控的。有一个思想,维护一个最大高度maxHeight记录遇到的最大高度,在内循环中自后向前遍历,这样一来,底的值始终在减小。这就意味着只需要考虑高的值,就能决定整个容器的面积。所以在遇到小于maxHeight的情况时,可以直接跳过本次循环,不需要再次计算面积。

 

方法二的解题代码:

 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     }
method_2

 

方法三:

  1. 方法二仍然是一个时间复杂度为O(n^2)的二次循环,但是还有优化的策略。
  2. 在一次内循环结束之后,进入下一次内循环,如果height[i]小于上一次的长度,可以直接跳过这次循环。这样一来看似二次循环的问题,可以通过从两侧向中间夹逼,转化为一次循环的问题。

 

方法三的解题代码:

 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     }
maxArea

 

相关链接:

https://leetcode.com/problems/container-with-most-water/

https://github.com/Gerrard-Feng/LeetCode/blob/master/LeetCode/src/com/gerrard/algorithm/medium/Q011.java

 

PS:如有不正确或提高效率的方法,欢迎留言,谢谢!

 

posted @ 2016-10-09 10:07  Gerrard_Feng  阅读(395)  评论(0编辑  收藏  举报