题目:
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
解法一(双重for循环枚举-时间复杂度超时):
采用双层for循环直接对所有结果依次进行枚举,遍历所有情况并记录容器能够容纳水的最大值,该方法时间复杂度为O(n^2),测试时超时,不作为推荐解法,仅列出作为和解法二方法的对比事例,如下为笔者代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int S = 0;
int h = 0;
int length = height.size();
int reuslt = 0;
for(int i=0; i<length; i++){
for(int j=i;j<length;j++){
h = min(height[i],height[j]);
S = (j-i)*h;
reuslt=std::max(reuslt,S);
}
}
return reuslt;
}
};
解法二(双指针):
初始时,左右指针分别指向数组的左右两端,计算初始容器容纳的水量S,S=H*W,其中W为左右指针两端之间宽度距离,H为容器的高度,取决于左右指针指向位置最小值的数值。迭代时,W逐渐缩小,为了保证最终的解为全局最大值解,需要移动左右指针对应数字较小的那个指针,以保证在W逐渐减小的过程中,H应逐渐增大。
如果我们移动数字较大的那个指针,那么前者【两个指针指向的数字中较小值】不会增加,而后者【指针之间的距离】会减小,那么这个乘积会减小。因此,通过移动数字较小的那个指针,直至两个指针重合,并记录在迭代过程中容器存储的最大容量。
为什么对应的数字较小的那个指针不可能再作为容器的边界了?
假设当前左指针和右指针指向的数分别为x和y,我们假设x≤y,同时,两个指针之间的距离为t。那么组成的容器的容量为min(x,y)*t=x*t,我们可以断定,如不保持左指针的位置不变,无论右指针在哪里容器的容量都不会超过x*t。注意,这里右指针只能向左移动,我们任意向左移动右指针,指向的数为y1,两个指针之间的距离为t1,那么显然有t1<t,并且min(x,y1)≤min(x,y)。因此有min(x,yt)*t1<min(x,y)*t,即无论我们怎么移动右指针,得到的容器边界容量都小于移动前容器的容量。也就是说,这个左指针对应的数不会作为容器的边界了,将左指针向右移动一个位置,才能保证新的指针数字计算得到的容器数值可能大于之前的最大数值,一句话概括:我们left++和right--都是为了尝试取到更多的水,如果短的板不动的话,取到的水永远不会比上次多(贪心算法)。如下为代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int area = min(height[l], height[r]) * (r - l);
ans = max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
};
笔者小记:
每次都移动自己最差的一边,虽然可能变得更差,但是总比不动(或者减小最强的一边)强,动最差的部分可能找到更好的结果,但是动另一边总会更差或者不变,兄弟们,这不是题,这是人生,逃离舒适圈!!