leetcode 11. 盛最多水的容器
- 1. leetcode 11. 盛最多水的容器
- 1. 题目详情
- 1. 原题链接
- 2. 基础框架
- 2. 解题思路
- 1. 题目分析
- 2. 算法原理
- 3. 时间复杂度
- 3. 代码实现
- 4. 知识与收获
1. leetcode 11. 盛最多水的容器
1. 题目详情
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
1. 原题链接
leetcode 11. 盛最多水的容器
2. 基础框架
● Cpp代码框架
class Solution {
public:
int maxArea(vector<int>& height) {
}
};
2. 解题思路
1. 题目分析
( 1 ) (1) (1) 题目要求的是水池的最大容积
2. 算法原理
( 1 ) (1) (1) 首先会想到使用暴力解法,使用两层for循环枚举出所有的情况,找出最大水的容积。但是时间复杂度 O ( n 2 ) O(n^2) O(n2)题目不给过,需要找规律优化效率。
(
2
)
(2)
(2) 使用对撞双指针算法:指针left
指向数组第一个元素,指针right
指向数组最后一个元素;此时容积可表示为
v
=
(
r
i
g
h
t
−
l
e
f
t
)
∗
m
i
n
(
h
e
i
g
h
t
[
l
e
f
t
]
,
h
e
i
g
h
t
[
r
i
g
h
t
]
)
v=(right - left) * min(height[left], height[right])
v=(right−left)∗min(height[left],height[right])
(
3
)
(3)
(3) 假设初始时
h
e
i
g
h
t
[
l
e
f
t
]
<
h
e
i
g
h
t
[
r
i
g
h
t
]
height[left] < height[right]
height[left]<height[right]:我们们的目的是找出水的最大容积
v
v
v。首先不管是
l
e
f
t
left
left往左移动,还是
r
i
g
h
t
right
right往右移动,
(
r
i
g
h
t
−
l
e
f
t
)
(right - left)
(right−left)一定是减小的;
当前
h
e
i
g
h
t
height
height较小的是
h
e
i
g
h
t
[
l
e
f
t
]
height[left]
height[left],那么如何确定是
l
e
f
t
left
left向右移动还是
r
i
g
h
t
right
right向左移动呢?
(
4
)
(4)
(4) 假设是
l
e
f
t
left
left向右移动(right不变):对于移动后的最小
h
e
i
g
h
t
height
height就有两种情况:
1.left移动后的
h
e
i
g
h
t
height
height<=原先的
h
e
i
g
h
t
height
height,此时
m
i
n
(
h
e
i
g
h
t
[
l
e
f
t
]
,
h
e
i
g
h
t
[
r
i
g
h
t
]
)
min(height[left],height[right])
min(height[left],height[right])是不变或减小的,那么
v
v
v一定是减小的;
2.left移动后height>原先的
h
e
i
g
h
t
height
height,此时
m
i
n
(
h
e
i
g
h
t
[
l
e
f
t
]
,
h
e
i
g
h
t
[
r
i
g
h
t
]
)
min(height[left], height[right])
min(height[left],height[right])是增大的(增大之后也不会超过
h
e
i
g
h
t
[
r
i
g
h
t
]
height[right]
height[right],因为是选择较小的
h
e
i
g
h
t
height
height计算),那么
v
v
v就可能是增大的。
( 5 ) (5) (5) 假设是 r i g h t right right向左移动(left不变):移动之后 h e i g h t [ r i g h t ] height[right] height[right]不管是增大、不变、还是减小, m i n ( h e i g h t [ l e f t , h e i g h t [ r i g h t ] ) min(height[left,height[right]) min(height[left,height[right])的值均不超过移动之前的值,即 v v v一定是减小的。
( 6 ) (6) (6) 综上,移动 h e i g h t height height较大的一方时, v v v不会超过当前的值;移动 h e i g h t height height较小的一方才可能得到更大的 v v v。
3. 时间复杂度
O ( n ) O(n) O(n)
左右双指针 l e f t left left和 r i g h t right right对撞而走,直到相遇结束,最终共同遍历了一遍整个数组。
3. 代码实现
class Solution {
public:
int maxArea(vector<int>& height) {
// v = s * h
int l = 0, r = height.size() - 1;
int maxWater = 0;
while(l < r){
int v = (r - l) * min(height[l], height[r]);
maxWater = max(maxWater, v);
if(height[l] > height[r]){
r--;
}
else{
l++;
}
}
return maxWater;
}
};
4. 知识与收获
(
1
)
(1)
(1) 双指针是非常优秀的一种算法,常常使算法时间复杂度降低一维。
(
2
)
(2)
(2) 常使用的有两种形式:
对撞双指针
,常见于数组中,核心思想来自快排。左指针left
指向起始元素,右指针right
指向最后一个元素。在循环中判断某些条件让左指针left
向右移动,右指针right
向左移动,直到left
和right
相遇停下来。
快慢双指针
,又叫龟兔赛跑法
,常见于数组、链表中,典例就是判断链表是否有环
。定义快慢指针slow
和fast
,通过让快慢指针分别以不同的速度移动来达到我们的目的。一般是快指针fast
一次走多步,慢指针slow
一次走一步。
(
3
)
(3)
(3) 双指针算法是一种思想,不局限于数组或链表。双指针不一定非要是定义两个指针,也可以是下标。
T h e The The E n d End End