pre
刷题的时候遇到的,没有想到最优解,而在网上搜索的时候大多只有代码而没有证明,所以我这里补上相关的逻辑证明
题目
(详细题目请直接点击文末的链接)[^题目]
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
思路
方案1:直接暴力查找,从左朝右寻找,这样做的时间复杂度为O(n^2),逻辑正确,但是代码执行效率太低。
class Solution {
public int maxArea(int[] height) {
if(height.length <= 0){
return 0;
}
int max = 0;
for(int i = 0 ; i < height.length; i++){
for(int j = i+1 ; j < height.length ; j++){
int width = j-i;
int squareHeight = height[i] > height[j] ? height[j] : height[i];
int contains = width*squareHeight;
if(contains > max){
max = contains;
}
}
}
return max;
}
}
方案2: 采用左右指针的方式,基于在缩短x轴上长度时只有提高左右边界最小高度才有可能增加容器容量的思路,我们每次吐出左右边界中的较小值,一遍遍历所有长度情况,最后得出全局最大值。
class Solution {
public int maxArea(int[] height) {
int max = 0 ;
int left = 0;
int right = height.length - 1;
while(left < right){
boolean isLeftHigh = height[left] > height[right];
int minHeight = isLeftHigh ? height[right]:height[left];
int contains = (right-left)*minHeight;
if(contains > max){
max = contains;
}
if(isLeftHigh){
right--;
}else{
left++;
}
}
return max;
}
}
方案2证明
这里采用反证法证明,证明思路来受到一个论坛老大的启发[^解析链接]。
推论1 :我们的左右指针至少一方会扫描到最大情况的边界
首先方案中存在一个最大方案,此时左右边界标注为 index_max_left,index_max_right。 因为方案在左右指针相等的时候停止,所有我们的左右指针至少其中一个会扫描到其中一个变量(左指针扫描到index_max_left 或者 右指针扫描到index_max_right)
推论2:一方指针扫描到最大情况的边界时,最大情况必定能够被检索到
假设左指针扫描到 index_max_left ,考虑如下情况
index_max_left ... A ... index_max_right ... B ...
如果右指针在 B的位置,
-
那么只有在 (index_max_left,B]中存在一个坐标,其取值大于等height[index_max_left ],
这种情况下右指针才可能扫描不到index_max_right,假设这个坐标是C,
由于contain(index_max_left ,C) > contain(index_max_left ,index_max_right ),
那么假设不成立,所以场景不成立
如果右指针 在 A的位置,
-
那么只有在 [0,index_max_left)中存在一个坐标,其取值大于等于height[index_max_right ],,
这种情况下右指针才可能扫描不到index_max_right,假设这个坐标是D,
由于contain(D,index_max_right )>contain(index_max_left ,index_max_right ),
那么假设不成立,场景不成立
综上所述,如果左指针能够扫描到index_max_left,那么右指针必定能够扫描到index_max_right.
同样的在右指针扫描到index_max_right时,左指针必定能够扫描到index_max_left.
结论:算法正确
结合推论1和推论2,该算法必定能够扫描到最大情况
总结
算法2的逆向证明不是太难,但是难点是怎么正向的构想出这个方案。很感谢大佬们分享的思路。
reference
[^题目] : https://leetcode-cn.com/problems/container-with-most-water/description/
[^解析链接] : leetcode.com/problems/container-with-most-water/discuss/6089/Anyone-who-has-a-O(N)-algorithm/7268