LeetCode: 11. 盛最多水的容器

题目

给定 n 个正整数 a1,a2,…,an,其中每个点的坐标用(i, ai)表示。 画 n 条直线,使得线 i 的两个端点处于(i,ai)和(i,0)处。请找出其中的两条直线,使得他们与 X 轴形成的容器能够装最多的水。

注意: 你不能倾斜容器,n 至少是2。

分析

思路一:暴力双遍历

时间复杂度为 O(n^2)

思路二:双指针

减少循环的核心思路是省去没有必要的遍历,并且确保所需的答案都能够遍历到。

  • 假设现在有一个容器,则容器的盛水量取决于容器的底和容器较短的那条高
  • 从最大的底长入手,即当容器的底等于数组的长度时,则容器的盛水量为较短边的长乘底
  • 可见只有较短边会对盛水量造成影响,因此移动较短边的指针,并比较当前盛水量和当前最大盛水量。直至左右指针相遇。

可以证明,如果移动较高的边,则盛水量只会变少;移动较低的边,则可以遍历到最大的情况。

证明

假设:该算法并没有遍历到容量最大的情况

  • 我们令容量最大时的指针为p_left和p_right。根据题设,我们可以假设遍历时左指针先到达p_left,但是当左指针为p_left时,右指针还没有经过p_right左指针就移动了
  • 已知当左指针停留在p_left时,它只有在两种场景下会发生改变
    1. 左指针和右指针在p_left相遇,则右指针一定在前往p_left的途中经过p_right,与题设矛盾
    2. 右指针位于p_right右侧且当前的值大于左指针。则在这种情况下,此时容器的盛水量比题设中最大的盛水量还要大,与题设矛盾

因此该算法的遍历一定经过了最大的盛水量的情况

解题

class Solution {
    public int maxArea(int[] height) {
        int left = 0;
        int right = height.length-1;
        int max = 0;
        while (left < right) {
            max = Math.max(max, Math.min(height[left], height[right]) * (right - left));
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return max;
    }
}

启示

  • 最直接的方法,多次循环一定要谨慎,思考有没有更好的方法。
  • 去除过多循环最好的方法就是双指针
  • 双指针的方向通常有两种
    1. 都从左向右,根据期间值的大小决定哪个指针右移以及右移到什么位置
    2. 从两端到中间,距离越来越短,知道相遇
    3. 以上两种方法都需要保证遍历到了最大函数值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值