LeetCode-----第十一题-----盛最多水的容器

盛最多水的容器

难度中等1494

给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (iai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (iai) 和 (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!),时间复杂度太高。另一种为双指针法,那么为什么可以使用双指针法。

指针移动规则与证明: 每次选定围成水槽两板高度 h[i]h[i],h[j]h[j] 中的短板,向中间收窄 11 格。以下证明:

(1)设每一状态下水槽面积为 S(i, j)S(i,j),(0 <= i < j < n)(0<=i<j<n),由于水槽的实际高度由两板中的短板决定,则可得面积公式           S(i, j) = min(h[i], h[j]) × (j - i)S(i,j)=min(h[i],h[j])×(j−i)。
(2)在每一个状态下,无论长板或短板收窄 11 格,都会导致水槽 底边宽度 -1−1:
         1)若向内移动短板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 可能变大,因此水槽面积 S(i, j)S(i,j) 可能增大。
         2)若向内移动长板,水槽的短板 min(h[i], h[j])min(h[i],h[j]) 不变或变小,下个水槽的面积一定小于当前水槽面积。
(3)因此,向内收窄短板可以获取面积最大值。换个角度理解:
         1)若不指定移动规则,所有移动出现的 S(i, j)S(i,j) 的状态数为 C(n, 2)C(n,2),即暴力枚举出所有状态。
         2)在状态 S(i, j)S(i,j) 下向内移动短板至 S(i + 1, j)S(i+1,j)(假设 h[i] < h[j]h[i]<h[j] ),则相当于消去了 {S(i, j - 1), S(i, j - 2), ... , S(i, i + 1)}S(i,j−1),S(i,j−2),...,S(i,i+1) 状态集合。而所有消去状态的面积一定 <= S(i, j)<=S(i,j):
               短板高度:相比 S(i, j)S(i,j) 相同或更短(<= h[i]<=h[i]);
               底边宽度:相比 S(i, j)S(i,j) 更短。
         3)因此所有消去的状态的面积都 < S(i, j)<S(i,j)。通俗的讲,我们每次向内移动短板,所有的消去状态都不会导致丢失面积最大值 。

 

参考代码:

       

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
#include <list>
#include <unordered_map>
#include <cstring>
#include <map>
#include <stdexcept>

using namespace std;

class Solution {
public:
	int maxArea(vector<int>& height) {
		if (height.empty())
			return 0;

		int left = 0;
		int right = height.size() - 1;
		int max_area = 0;
		
		//使用双指针法,两个指针的移动,主要考虑当前值的大小
		while (left < right)
		{
			if ( min(height[left], height[right]) * (right - left) > max_area)
			{
				//面积的计算公式:S = min(h[i], h[j]) × (j - i) 
				max_area = min(height[left], height[right]) * (right - left);
			}
			//如果左边小于右边,那么左指针向右移动
			if (height[left] < height[right])
			{
				++left;
			}
			else if (height[left] > height[right])//如果左边大于右边,那么右指针向左移动
			{
				--right;
			}
			else if (height[left] == height[right])//如果左边等于右边
			{
				//比较两个指针的下一个值,谁大就谁移动
				if ((left + 1 < right - 1) && height[left + 1] > height[right - 1])
					++left;
				else if ((left + 1 <= right - 1) && height[left + 1] <= height[right - 1])
					--right;
				else if (left + 1 > right - 1)  //考虑一下边界情况,如果两个指针指过头了就走出循环
					break;
			}	
		}
		return max_area;
	}
};
int main()
{
	Solution solution;
	vector<int> my_array;
	int arr[] = { 1, 2, 1 };

	my_array.assign(arr, arr + 3);

	cout << solution.maxArea(my_array) << endl;

	system("pause");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值