LeetCode hot100系列——11. container-with-most-water(双指针、短板题型)

题目描述

给定 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,8,6,2,5,4,8,3,7]
输出: 49

思路分析

暴力法

这个暴力法可太暴力了,没有什么意义,就是两两遍历所有的边,然后乘出来一个面积,找出最大的,
时间复杂度

$$ O(n^2) $$

空间复杂度

$$ O(1) $$

双指针法

这种方法是基于对题目特性的分析,因为如果完全没有规律,只是要求最大面积,那肯定只能是暴力法。
但是题目为什么要特意强调盛水?盛水这种物理行为有什么特殊的规律?就好比有序数组的有序性导致可以使用二分查找,盛水的具有短板效应,所谓短板效应就是容器能盛多少水完全取决于最短的那个边

基于此特性,可以归纳出以下几种情况:

如果从高的这边往低的这边走,不论遇到更高的还是更低的都没有意义
高的遇到低的,和上面同理,宽度本来就低了,高度也低,面积肯定更小,没有意义
高的那侧遇到更高的,可是由于短板效应,面具取决短板,所以围起来的面积不受高的一侧的影响

综上,只有从短板向高的一侧遍历,并且只有短板升高了这种情况是存在面积变大的可能性的,因此循环就可以变成一层循环

代码实现

暴力法跳过

class Solution {
    public int maxArea(int[] height) {
        int left = 0;
        int right = height.length - 1;
        int max = 0;
        int theLower = 0;
        while(left < right) {
            int lowerSide;
            int lower;
            if(height[left] < height[right]) {
                lowerSide = left;
                lower = height[left];
            } else {
                lowerSide = right;
                lower = height[right];
            }
            if(lower > theLower) {
                // probably update max
                int area = lower * (right - left);
                max = Math.max(max, area);
                theLower = lower; // update theLower
            }
            if(lowerSide == left) {
                left++;
            } else {
                right--;
            }
        }
        return max;
    }
}

总结

说实话这种问题没有固定套路,纯靠数学和物理学的感觉,思路大概就是遇事不决先暴力,然后再思考盛水这种东西有什么特点,想到短板效应,归纳出所有情况;或者看了解题思路以后背吧,遇到类似的盛水问题的时候可以想一下短板效应
类似题目trapping-rain-water

你可能感兴趣的:(leetcode,java)