经典题型
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。说明:你不能倾斜容器。
实现思路
参考自B站零神(零茶山艾府)视频【基础算法精讲 2】
以数组 [1, 8, 6, 2, 5, 4, 8, 3, 7] 为例
首先根据木桶原理我们知道,能容纳水的高度,取决于两端高度较短的那条线,所以我们可以得出
可容纳水的体积计算公式:两端最小高度 ∗ 两端之间的距离
如果我们移动高度较大的一端,那么前者「两端最小高度」不会增加,而后者「两端之间的距离」会减小,那么这个乘积会减小。因此,我们移动高度较高那一端是不合理的。因此,我们移动高度较小的那一端。
- 开始左右指针分别指向数组两端1和7,此时水的体积为 v = 1*(9- 0 - 1) = 9
- 左边高度更小,则左指针右移,指向8,此时左右指针分别指向8,7
- 水的体积为Max(v, 7 * (9 - 1 - 1)) = 49
- 此时,右边高度更小,则右指针左移,左右指针分别指向8,3
- 水的体积为Max(v, 3 * (8 - 1 - 1)) = 49
- 同样的逻辑一直计算下去,直至左右指针相遇
- 最终得到,此数组能容纳水的最大体积为49
实现代码
class Solution {
public int maxArea(int[] height) {
int n = height.length, res = 0;
int left = 0, right = n - 1;
while (left < right) {
// 容纳水的体积,等于两端高度较短的那条线的高度 * 两线之间的宽度
res = Math.max(res, Math.min(height[left], height[right]) * (right - left));
// 如果左边高度小于右边高度,则继续遍历左边的线
if (height[left] <= height[right]) {
left++;
} else { // 反之遍历右边的线
right--;
}
}
return res;
}
}
相似题型
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
实现思路
类比于“最大可容纳水的体积”的思路
能接的雨水量还是取决于高度小的一端,而每根柱子所在位置能接多少雨水,又是取决于其高度较短一端的高度最大值。
以数组 [0,1,0,2,1,0,1,3,2,1,2,1] 为具体例子来看
- 开始左右指针分别指向左右两端,高度为0和1,此时记录前缀最大值preMax = 0,后缀最大值sufMax = 1
- 然后来计算下标为0到1的柱子和下标为n-1到n-2的柱子的接水量
- 由于前缀最大值小于后缀最大值,那么我们就能确定下标为0到1的柱子的接水量了,由于能接的雨水量还是取决于高度小的一端,所以后缀最大值的增大不会影响到接水量;而对于下标为n-1到n-2的柱子,前缀最大值的增大,可能会使得接水量发生变化
- 此时下标为0到1的柱子的接水量 = preMax - 当前柱子高度 = 0,且左指针右移
- 同理可以得到前缀最大值大于后缀最大值,可以计算右边柱子的接水量,且右指针左移
- 总结一下就是,遍历过程中分别记录左右两边的最大高度,如果preMax <= sufMax,则计算左边柱子的接水量,且左指针右移;反之,则计算右边柱子的接水量,且右指针左移,直至左右指针相遇
实现代码
class Solution {
public int trap(int[] height) {
int res = 0;
int n = height.length;
// 分别记录前缀最大值,和后缀的最大值
int preMax = height[0], sufMax = height[n - 1];
int left = 0, right = n - 1;
while (left < right) {
// 检查当前左右指针指向元素是否为前后缀的最大值
preMax = Math.max(height[left], preMax);
sufMax = Math.max(height[right], sufMax);
// 当前前缀最大值小于等于后缀最大值,则可计算左边的可接雨水
if (preMax <= sufMax) {
res += preMax - height[left++];
}
// 当前前缀最大值大于后缀最大值,则可计算右边的可接雨水
if (sufMax < preMax) {
res += sufMax - height[right--];
}
}
return res;
}
}