乐趣区

LeetCode-hot100系列11-containerwithmostwater双指针短板题型

题目描述

给定 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

退出移动版