LeetCode 407 - Trapping Rain Water II
[LeetCode]Container With Most Water, 解题报告 - 小一的专栏 - 博客频道 - CSDN.NET
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.
https://leetcode.com/articles/container-most-water/
https://discuss.leetcode.com/topic/3462/yet-another-way-to-see-what-happens-in-the-o-n-algorithm
https://discuss.leetcode.com/topic/25004/easy-concise-java-o-n-solution-with-proof-and-explanation/
https://rafal.io/posts/leetcode-11-container-with-most-water.html
is maximized. At every step, if
Initially we can assume the result is 0. Then we scan from both sides. If leftHeight < rightHeight, move right and find a value that is greater than leftHeight. Similarily, if leftHeight > rightHeight, move left and find a value that is greater than rightHeight. Additionally, keep tracking the max value.
http://www.voidcn.com/blog/luke2834/article/p-6071589.html
X. Brute Force
wlog meaning
http://artofproblemsolving.com/wiki/index.php?title=Without_loss_of_generality
[LeetCode]Container With Most Water, 解题报告 - 小一的专栏 - 博客频道 - CSDN.NET
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.
https://leetcode.com/articles/container-most-water/
We have to maximize the Area that can be formed between the vertical lines using the shorter line as length and the distance between the lines as the width of the rectangle forming the area.
The intuition behind this approach is that the area formed between the lines will always be limited by the height of the shorter line. Further, the farther the lines, the more will be the area obtained.
We take two pointers, one at the beginning and one at the end of the array constituting the length of the lines. Futher, we maintain a variable to store the maximum area obtained till now. At every step, we find out the area formed between them, update and move the pointer pointing to the shorter line towards the other end by one step.
Initially we consider the area constituting the exterior most lines. Now, to maximize the area, we need to consider the area between the lines of larger lengths. If we try to move the pointer at the longer line inwards, we won't gain any increase in area, since it is limited by the shorter line. But moving the shorter line's pointer could turn out to be beneficial, as per the same argument, despite the reduction in the width. This is done since a relatively longer line obtained by moving the shorter line's pointer might overcome the reduction in area caused by the width reduction.
https://discuss.leetcode.com/topic/25004/easy-concise-java-o-n-solution-with-proof-and-explanation/
https://rafal.io/posts/leetcode-11-container-with-most-water.html
The naive version would solve this in time. It can be improved as follows to linear time:
Keep two indices and , that start on both ends of the array to process,
height
. Our final goal is to find,, such thatheight[i] < height[j]
, move i
up by one. Otherwise move j
down by one, until the two pointers meet. We now show that this gives the maximal answer. Suppose we just computed , and height[i] < height[j]
. If we increase by one, we will ignore all the values for . We now show that . Since height[i] < height[j]
, is bounded by the height[i]
term, and since , it follows that , and we can safely ignore all of the and move on to inspecting . The case with height[i] > height[j]
is symmetric.Initially we can assume the result is 0. Then we scan from both sides. If leftHeight < rightHeight, move right and find a value that is greater than leftHeight. Similarily, if leftHeight > rightHeight, move left and find a value that is greater than rightHeight. Additionally, keep tracking the max value.
public int maxArea(int[] height) { if (height == null || height.length < 2) { return 0; } int max = 0; int left = 0; int right = height.length - 1; while (left < right) { max = Math.max(max, (right - left) * Math.min(height[left], height[right])); if (height[left] < height[right]) left++; else right--; } return max; } |
- The widest container (using first and last line) is a good candidate, because of its width. Its water level is the height of the smaller one of first and last line.
- All other containers are less wide and thus would need a higher water level in order to hold more water.
- The smaller one of first and last line doesn't support a higher water level and can thus be safely removed from further consideration.
def maxArea(self, height):
i, j = 0, len(height) - 1
water = 0
while i < j:
water = max(water, (j - i) * min(height[i], height[j]))
if height[i] < height[j]:
i += 1
else:
j -= 1
return water
Further explanation:
Variables
i
and j
define the container under consideration. We initialize them to first and last line, meaning the widest container. Variable water
will keep track of the highest amount of water we managed so far. We compute j - i
, the width of the current container, and min(height[i], height[j])
, the water level that this container can support. Multiply them to get how much water this container can hold, and update water
accordingly. Next remove the smaller one of the two lines from consideration, as justified above in "Idea / Proof". Continue until there is nothing left to consider, then return the result.
当从两边向中间考虑时,乘水的面积是由(两端较小的高度)×(两个板之间的宽度)决定的。
假定初始的盛水面积为ans=0,lh为左边的高度,rh为右边的高度,如果lh < rh, 则向右运动,寻找第一个比当前lh大的左节点。同理,如果lh > rh,则向左运动,寻找第一个比当前rh大的右节点。
截止条件为坐标L >= R。
- public int maxArea(int[] height) {
- int i, j, lh, rh, area, tmp, len = height.length;
- lh = height[0];
- rh = height[len - 1];
- area = 0;
- i = 0;
- j = len - 1;
- while (i < j) {
- tmp = Math.min(lh, rh) * (j - i);
- if (tmp > area) {
- area = tmp;
- }
- if (lh < rh) {
- while (i < j && height[i] <= lh) {
- i ++;
- }
- if (i < j) {
- lh = height[i];
- }
- } else {
- while (i < j && height[j] <= rh) {
- j --;
- }
- if (i < j) {
- rh = height[j];
- }
- }
- }
- return area;
- }
http://www.voidcn.com/blog/luke2834/article/p-6071589.html
- 我们先把柱子从低到高排序,然后在遍历这个有序集合中的每个柱子时,我们维护两个变量l,r记录比该柱子高的最左和最右位置。
- 具体的方法,是用一个mark数组已经遍历过哪些位置的柱子,每遍历第i个柱子后,令mark[i]=1,然后让l和r分别向右、向左移动到第一个mark不是1的位置
- 这样移动总共只有n次吗,总的时间复杂度是排序的O(nlogn)
vector<pair<int, int> > vec; int maxArea(vector<int>& height) { for(int i = 0; i<height.size(); i++){ vec.push_back({height[i], i}); } vector<int> mark(vec.size(), 0); sort(vec.begin(), vec.end()); int l = 0, r = height.size() - 1; int ans = 0; for (int i=0;i<vec.size();i++){ ans = max(ans, vec[i].first * (r - vec[i].second)); ans = max(ans, vec[i].first * (vec[i].second - l)); mark[vec[i].second] = 1; while (mark[l] == 1) l++; while (mark[r] == 1) r--; } return ans; }
算法2:时间复杂度O(nlogn)。
构建结构体包含height和height在原数组中的位置
struct Node
{
int height;
int index;
{
int height;
int index;
};
对该结构体数组按照height的值递增排序,假设排序后的数组为vec.
假设f[i] 表示数组vec[i,i+1,…]内所有height按照原来的位置顺序排列好以后的最大水量
那么f[i-1]可以在O(1)时间内计算出来:因为vec[i-1].height 小于vec[i,i+1,…]内的所有height,所以以vec[i-1].index为边界的容器高度为vec[i-1].height,最大水量只需要分别计算vec[i,i+1,…]内按原始位置排列最前面和最后面的height,取两者的较大值。即下图中,黑色是最低的,要计算以黑色为边界的容器的最大水量,只需要分别和第一个和最后一个计算,去两者较大值
struct
Node
{
int
height;
int
index;
Node(
int
h,
int
i):height(h),index(i){}
Node(){}
bool
operator < (
const
Node &a)
const
{
return
height < a.height;
}
};
public
:
int
maxArea(vector<
int
> &height) {
int
res = 0, n = height.size();
if
(n <= 1)
return
0;
vector<Node>vec(n);
for
(
int
i = 0; i < n; i++)
{
vec[i].index = i;
vec[i].height = height[i];
}
sort(vec.begin(), vec.end());
int
start = vec[n-1].index, end = start;
//记录已经处理完的height的原始位置的左右端点
for
(
int
i = n-2; i >= 0 ; i--)
{
start = min(start, vec[i].index);
end = max(end, vec[i].index);
res = max(res, max(vec[i].height*(vec[i].index - start), vec[i].height*(end - vec[i].index)));
}
return
res;
}
算法1:枚举容器的两个边界,时间复杂度O(n^2)。大数据超时
int
maxArea(vector<
int
> &height) {
int
res = 0, n = height.size();
for
(
int
i = 0; i < n; i++)
//左边界
for
(
int
j = i+1; j < n; j++)
//右边界
{
int
tmp = (j-i)*min(height[i],height[j]);
if
(res < tmp)res = tmp;
}
return
res;
}
对上面的稍加改进,根据当前的已经计算出来的结果以及左边界的值,可以算出容器右边界的下界。不过还是超时
int
maxArea(vector<
int
> &height) {
int
res = 0, n = height.size();
for
(
int
i = 0; i < n; i++)
//左边界
{
if
(height[i] == 0)
continue
;
for
(
int
j = max(i+1, res/height[i]+i); j < n; j++)
//右边界
{
int
tmp = (j-i)*min(height[i],height[j]);
if
(res < tmp)res = tmp;
}
}
return
res;
}
X. Brute Force
In this case, we will simply consider the area for every possible pair of the lines and find out the maximum area out of those.
public int maxArea(int[] height) { int maxarea = 0; for (int i = 0; i < height.length; i++) for (int j = i + 1; j < height.length; j++) maxarea = Math.max(maxarea, Math.min(height[i], height[j]) * (j - i)); return maxarea; }
http://artofproblemsolving.com/wiki/index.php?title=Without_loss_of_generality
Without loss of generality is a term used in proofs to indicate that an assumption is being made that does not introduce new restrictions to the problem. For example, in the proof of Schur's Inequality, one can assume that without loss of generality because the inequality is symmetric in , and . Without loss of generality is often abbreviated WLOG or WOLOG. Be sure not to write WLOG when you mean "with loss of generality"!
Read full article from [LeetCode]Container With Most Water, 解题报告 - 小一的专栏 - 博客频道 - CSDN.NET