盛最多水的容器
题目信息:
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
题目参考:力扣:11. 盛最多水的容器
题目思路(环境来自力扣OJ的C++):
暴力枚举:
先固定一个数,枚举其与后面的数组成的容器的容量,标记最大的返回。
双指针:
双指针是在暴力枚举的基础上进行优化的。
我们发现:
-
容量的大小公式为 底(宽) × 高 ,使用 对撞指针 移动时,底一定会减小。
-
如果移动 高度较高的指针 ,底(减小) × 高(减小),结果一定比原来的容量小,可以优化这步。
-
如果移动 高度较低的指针 ,底(减小) × 高(减小或增大),这时结果是不确定的,需要遍历。
-
对撞指针移动时,并不会出现回退的情况,也就是指针移动规则具有单调性,则双指针的使用有效。
这也就是说,双指针优化掉部分不需要的遍历,降低了时间复杂度。
移动高度较高的指针
移动高度较低的指针
复杂度:
暴力枚举:
时间复杂度:(n × n)
空间复杂度:(1)
双指针:
时间复杂度:(n)
空间复杂度:(1)
代码与注释:
暴力枚举:
cpp
class Solution {
public:
int maxArea(vector<int>& height) { // 暴力会超时 O(n ^ 2)
int ret = 0;
int sz = height.size();
for (int i = 0; i < sz; ++i) // 先固定一个数
{
for (int j = sz - 1; j > i; --j) // 分别计算其与后面的数
{ // 组成的容器的容量
int wide = j - i;
int high = min(height[i], height[j]);
ret = max(ret, wide * high); // 标记最大的
}
}
return ret; // 返回
}
};
双指针:
cpp
class Solution {
public:
int maxArea(vector<int>& height) { // 对撞指针 O(n)
int left = 0;
int right = height.size() - 1;
int ret = 0;
while (left < right) // 对撞则遍历完
{
int wide = right - left;
int high = min(height[left], height[right]);
ret = max(ret, wide * high); // 标记最大的
if (height[left] < height[right]) // 移动高度较低的指针
{
++left;
}
else
{
--right;
}
}
return ret;
}
};