本节我们继续来讲讲一道经典的滑动窗口问题,就是 leetCode11. 盛最多水的容器。
本题目看似挺复杂的,其实只要把思路理清楚了,代码实现可以说是相当简单。
我们可以设两指针为i,j,指向的水槽板高度分别为 h[i] 和 h[j] ,此状态下水槽板的面积为S(i,j) 。由于可以容纳水的高度有两木板中的短板决定,因此我们有以下面积公式:
s( i , j ) = min( h[i] , h[j] ) x ( j - i )
在每个状态下,无论长板或短板向中间收窄一格,都会导致水槽底边宽度-1变短:
- 若向内移动短板,水槽的短板min(h[i],h[j])可能变大,因此下个水槽的面积可能增大。
- 若向内移动长板,水槽的短板mi(h[i],h[j])不变或变小,因此下个水槽的面积一定变小。
所以,只要初始化双指针分列水槽的两端,循环每轮将短板向内移动一格,并每次更新最大面积,直到两指针相遇,代码实现如下:
- 普通版本
public int maxArea(int[] height) {
int left = 0, right = height.length - 1; // 初始化左右指针,分别指向数组的开头和结尾
int s = 0; // 用于存储当前计算的容器面积
int max = 1; // 用于存储最大容器面积的初始值
// 使用双指针法,从两侧向中间靠拢寻找最大容器面积
while (left < right) {
// 计算当前左右指针所围成的容器面积,取较小的高度乘以宽度
s = Math.min(height[left], height[right]) * (right - left);
// 更新最大容器面积
max = Math.max(max, s);
// 移动指针的规则是向内移动较小高度的指针,以期望找到更高的边界
if (height[left] > height[right]) {
right--; // 右侧高度较小,向内移动右指针
} else {
left++; // 左侧高度较小或相等,向内移动左指针
}
}
return max;
}
- 简约版本
public int maxArea(int[] height) {
int i = 0, j = height.length - 1; // 初始化左右指针,分别指向数组的开头和结尾
int res = 0; // 用于存储当前计算的容器面积的最大值
// 使用双指针法,从两侧向中间靠拢寻找最大容器面积
while (i < j) {
// 计算当前左右指针所围成的容器面积,取较小的高度乘以宽度
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]) : // 如果左侧高度较小,向内移动左指针
Math.max(res, (j - i) * height[j--]); // 如果右侧高度较小,向内移动右指针
}
return res; // 返回最大容器面积
}