LeetCode11. Container With Most Water(思维题:选择左右边使得容器所盛水最多)
来源:互联网 发布:北洋标签打印机软件 编辑:程序博客网 时间:2024/04/28 05:48
这道题,我独立想了好久!!!!一道很好的思维题!!!!!!!!
思路1是自己的求解方法O(nlogn),思路2是网上最优的解法O(n),暴利O(n^2)肯定超时!
题目链接:https://leetcode.com/problems/container-with-most-water/
Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant(倾斜) the container.
解题思路1:性质O(nlogn)
从结论思考有这样一条性质不难发现:如果l,r是最优的边,那么l左边一定不存在比它大的,同理,r右边也不存在比它大的;
所以,我们可以预处理出所有可能是l,r的边,条件是比它左(右)边的最大值要大。最终得到的肯定是如下形状的数组(中心是全局的最大值):
接下来,需要考虑的是怎样找到全局的最优值,我的做法是(默认左低右高)从左半边开始遍历,同时二分右边找到大于等于左边元素的值,两条边构成容器,计算面积更新ans;然后反过来,(默认左高右低),从右半边开始遍历,同时二分左边找到大于等于右边元素的值,这两边也构成容器,计算面积再更新ans;复杂度O(nlogn)!
代码中注意二分bsearch()返回的是数组中大于等于所找元素v的值在初始height[]容器中的下标!
参考代码1:
#define eps 1e-8struct node{ int h,p; node(int H,int P):h(H),p(P){}};vector<struct node>v1;vector<struct node>v2;int bsearch1(int v){ int l = 0,r = v2.size(),m; while(l < r){ m = (l+r) >> 1; if(v2[m].h == v) return v2[m].p; if(v2[m].h < v) l = m+1; else r = m ; } return v2[r].p;}int bsearch2(int v){ int l = 0,r = v1.size(),m; while(l < r){ m = (l+r) >> 1; if(v1[m].h == v) return v1[m].p; if(v1[m].h < v) l = m+1; else r = m; } return v1[r].p;}class Solution {public: int maxArea(vector<int>& height) { int n = height.size(); int dp[2][n];//dp[0][]从左到右最大值,dp[1][]从右到左最大值 dp[0][0] = height[0]; for(int i = 1;i < n;++ i){ dp[0][i] = max(height[i],dp[0][i-1]); } v1.clear(); v1.push_back(node(height[0],0)); for(int i = 1;i < n;++ i){ if(height[i] > dp[0][i-1]) v1.push_back(node(height[i],i)); } dp[1][n-1] = height[n-1]; for(int i = n-2;i >=0; -- i){ dp[1][i] = max(height[i],dp[1][i+1]); } v2.clear(); v2.push_back(node(height[n-1],n-1)); for(int i = n-2;i >= 0;-- i){ if(height[i] > dp[1][i+1]) v2.push_back(node(height[i],i)); } int ans = 0; for(int i = 0;i < v1.size();++ i){//左低右高 int area = v1[i].h*(bsearch1(v1[i].h) - v1[i].p); ans = max(ans,area); } for(int i = 0;i < v2.size();++ i){//左高右低 int area = v2[i].h*(v2[i].p - bsearch2(v2[i].h)); ans = max(ans,area); } return ans; }};
解题思路2:单调性O(n)
这个我感觉是一种单调性,我开始的思路是l不动,r增加,发现面积的变化是不确定的;所以换一种思路,让r减小,你会发现:当height[l] < height[r]时,height[l] 与 height[l+1 ... r-1]构成的容器体积都比height[r]小,这就是一种单调性,可以作O(n^2) -> O(n)的多余情况的剔除!
初始时默认l = 0,r = height.size() - 1,读者可以在草稿纸上作各种情景的模拟,你会发现有这样的比较过程:
首先,纯暴力,我们要比较C(n,2) = n * (n - 1) / 2种情况;
然后,初始时比较height[l = 0] 与 height[r = n - 1],不管height[l]、height[r]孰大孰小,我们总会剔除n - 2种组合,是不是?
不妨假设height[l] < height[r].接着,除了(height[l],height[r])这种组合,height[l]已经不适合作左边,那么看下一种情况l++;同样地,比较当前的height[l] 、 height[r],我们又会剔除 n - 3种组合;
依次类推,直到最后,两条临近的边,不用剔除;
第一次:比较O(1) + 剔除O( n - 2 )
第二次:比较O(1) + 剔除O( n - 3 )
第三次:比较O(1) + 剔除O( n - 4 )
....
第n-2次:比较O(1) + 剔除O( 1 )
第n-1次:比较O(1) + 不剔除
每次都会计算一个组合,并剔除多种组合,所以,总共比较了(n-1) + sigma(n-2,n-3,0) = 0+1+....+n-1 = n*(n-1)/2 = C(n,2)种组合,这正好与纯暴力比较的组合数目一致。
因为利用单调性,每次比较都剔除了多种组合,总的复杂度降低了!
参考代码2:
class Solution {public: int maxArea(vector<int>& height) { int n = height.size(); int l = 0, r = n - 1; int ans = 0; while(l < r){ ans = max(ans,min(height[l],height[r])*(r-l)); if(height[l] < height[r]){ ++ l; } else -- r ; } return ans; }};
P.S.自己思考总结的东西,记忆是最深刻\收获是最多的,光看别人的题解,没有多少算法能力的提高~
- LeetCode11. Container With Most Water(思维题:选择左右边使得容器所盛水最多)
- Container With Most Water (容器中盛最多的水)
- Leetcode11: Container with most water
- [LeetCode11]Container With Most Water
- LeetCode11:Container With Most Water
- leetcode11 Container With Most Water
- leetcode11 Container With Most Water
- LeetCode11:Container With Most Water
- Leetcode11 Container With Most Water
- leetcode11:Container With Most Water
- LeetCode11: Container With Most Water
- leetcode11. Container With Most Water
- leetcode11. Container With Most Water
- LeetCode11-Container With Most Water
- LeetCode11. Container With Most Water
- LeetCode11. Container With Most Water
- LeetCode11. Container With Most Water
- leetcode11. Container With Most Water
- frameworks.sh: No such file or directory
- oracle查询包含大小写的数据
- 用H5 Canvas 绘画箭头
- hah
- tomcat异常关闭后再启动报错:IOException while loading persisted sessions: java.io.EOFException
- LeetCode11. Container With Most Water(思维题:选择左右边使得容器所盛水最多)
- 使用Powermock实现单元测试,提高单元测试覆盖率
- 面试必备题之一(质数)
- Java判断整数和浮点数
- Windows 系统下Git安装图解
- 编译unity-mono( 写给超级初学者的朋友)
- yii2.0多文件上传
- 负载均衡之反向代理
- 基于Docker实现DevOps的一些探索