11. 盛最多水的容器
给你 n
个非负整数 a1,a2,...,a
n
,每个数代表坐标中的一个点 (i, ai)
。在坐标内画 n
条垂直线,垂直线 i
的两个端点分别为 (i, ai)
和 (i, 0)
。找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1] 输出:1
示例 3:
输入:height = [4,3,2,1,4] 输出:16
示例 4:
输入:height = [1,2,1] 输出:2
提示:
n = height.length
2 <= n <= 3 * 104
0 <= height[i] <= 3 * 104
法一:极其朴素的暴力解法(时间超限,中等题目用暴力就是作死)
class Solution { public: int maxArea(vector<int>& height) { int n = height.size(), Max = 0; for(int i=0; i<n; i++){ for(int j=1; j<n; j++){ Max = max(Max, (j-i)*min(height[i], height[j])); } } return Max; } };
法二:双指针解法
看了答案我哭了~
思路:
首先,明确一点,结果应该是max{min(height[j], height[i]) * (j-i)}。
现在考虑设置两个指针i和j,分别指向首元素和尾元素。双指针代表的是可以作为容器左边界和右边界的范围。每次都把指向较小元素的那个指针指向下一个元素,就表示这个指针不可能再作为容器的边界了。在遍历过程中就能得到结果。
为啥每次都把指向较小元素的那个指针指向下一个元素呢?
不妨设height[i] 等于 x, height[j] = y, 且x<y. 那么,当前容器容量是min(height[j], height[i]) * (j-i) = x*(j-i);
假设接下来j向左移,height[j-1] = y1. (j-i)一定会变小
1、y1<=y, min(height[j], height[i]) * (j-i) = min(x, y1)*(j-i) <= min(x, y)*(j-i)
2、y1>y, min(height[j], height[i]) * (j-i) = min(x, y1)*(j-i) = x*(j-i) = min(x,y)*(j-i)
无论怎么移动右指针,得到的容器容量都小于前容器的容量。
所以,我们应该移动height值较小的那个指针。
class Solution { public: int maxArea(vector<int>& height) { int n = height.size(), Max = 0; int i = 0, j = n-1; while(i < j){ Max = max(Max, min(height[i], height[j])*(j-i)); if(height[i] < height[j]) i++; else j--; } return Max; } };
法三: 动态规划
递归法,可以改成法二迭代,这里主要理解思想。
class Solution { public: int getResult(int l, int r, vector<int> &h){ if(r-l == 1) return min(h[r], h[l]); int Max = min(h[r], h[l])*(r-l), subMax = 0; if(h[l] < h[r]){ subMax = getResult(l+1, r, h); } else subMax = getResult(l, r-1, h); return max(Max, subMax); } int maxArea(vector<int>& height) { int n = height.size(), Max = 0; Max = getResult(0, n-1, height); return Max; } };