【LeetCode】P11 盛最多水的容器

P11 盛最多水的容器

题目链接:11. 盛最多水的容器.

题目描述

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

题解

方法一:双指针

思路

首先将使用双指针来解决这个问题的方法讲解一遍,再证明这种做法的正确性。

设置两个指针 leftright(它们所指向的元素值分别记为 xy),开始时,left 指向数组的第一个元素,right 指向数组的最后一个元素,两指针指向的位置代表容器的左右边界,由以下公式计算出此时的可以容纳的水量:

  • m i n ( x , y ) ⋅ ( r i g h t − l e f t ) min(x,y)\cdot(right-left) min(x,y)(rightleft)

然后将 xy 中较小值对应的指针向另一个指针的方向移动(至于为什么要移动较小值对应的指针稍后会做出证明),然后按照上面的公式计算出此时可以容纳的水量。继续移动xy 较小值对应的指针,每移动一次都要计算出此时可以容纳的水量,直到 leftright 相遇。最终的答案即为每一次以 leftright 为左右边界计算出的可以容纳水量中的最大值。

在这里插入图片描述


为什么要移动较小值对应的指针?

证明:记当前左右指针指向的元素值分别为 x 0 x_0 x0 y 0 y_0 y0,不妨假设 x 0 < y 0 x_0<y_0 x0<y0,左右指针之间的距离为 d 0 d_0 d0,则此时可容纳的水量为: V 0 = m i n ( x 0 , y 0 ) ⋅ d 0 = x 0 ⋅ d 0 V_0=min(x_0,y_0)\cdot d_0=x_0\cdot d_0 V0=min(x0,y0)d0=x0d0

若将右指针向左移动,使其指向 y 1 y_1 y1,则此时可容纳的水量为 V 1 = m i n ( x 0 , y 1 ) ⋅ ( d 0 − 1 ) V_1=min(x_0,y_1)\cdot (d_0-1) V1=min(x0,y1)(d01)

  • y 1 < x 0 y_1<x_0 y1<x0,则 m i n ( x 0 , y 1 ) = y 1 < x 0 min(x_0,y_1)=y_1<x_0 min(x0,y1)=y1<x0
  • y 1 ⩾ x 0 y_1\geqslant x_0 y1x0,则 m i n ( x 0 , y 1 ) = x 0 min(x_0,y_1)=x_0 min(x0,y1)=x0
    V 1 = m i n ( x 0 , y 1 ) ⋅ ( d 0 − 1 ) ⩽ x 0 ⋅ ( d 0 − 1 ) V_1=min(x_0,y_1)\cdot (d_0-1)\leqslant x_0\cdot(d_0-1) V1=min(x0,y1)(d01)x0(d01)

    V 1 < V 0 V_1<V_0 V1<V0

所以移动右指针(或者说移动较大值对应的指针),一定会导致得到的可容纳的水量小于未移动之前的可容纳水量。

若将左指针向右移动,使其指向 x 1 x_1 x1,则此时可容纳的水量为 V 2 = m i n ( x 1 , y 0 ) ⋅ ( d 0 − 1 ) V_2=min(x_1,y_0)\cdot (d_0-1) V2=min(x1,y0)(d01)

  • x 1 ⩽ x 0 x_1\leqslant x_0 x1x0,则 m i n ( x 1 , y 0 ) = x 1 ⩽ x 0 min(x_1,y_0)=x_1\leqslant x_0 min(x1,y0)=x1x0
  • x 1 > x 0 x_1> x_0 x1>x0,则 m i n ( x 1 , y 0 ) > x 0 min(x_1,y_0)>x_0 min(x1,y0)>x0

    可能出现
    V 2 > V 0 V_2>V_0 V2>V0

所以移动左指针(或者说移动较小值对应的指针),可能会得到的可容纳的水量大于未移动之前的可容纳水量。

所以我们应该移动较小值对应的指针。

算法

class Solution {
public:
	int maxArea(vector<int>& height) {
		int size=height.size();
		int left=0;
		int right=size-1;
		int ans=0;
		while(left<right){
			int temp=(right-left)*min(height[left],height[right]);
			ans=temp>ans?temp:ans;
			if(height[left]>height[right]){
				--right;
			}
			else{
				++left;
			}
		}
		return ans;
	}
};

复杂度分析

假设数组中元素的个数为 n n n

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值