盛最多水的容器
原题目链接:点击跳转
给定一个长度为 n 的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和(i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
提示:
- n == height.length
- 2 <= n <= 105
- 0 <= height[i] <= 104
题解
设两指针 i
, j
,指向的水槽板高度分别为 h[i]
, h[j]
,此状态下水槽面积为 S(i,j)
。由于可容纳水的高度由两板中的 短板 决定,因此可得如下 面积公式 :
S ( i , j ) = m i n ( h [ i ] , h [ j ] ) × ( j − i ) S(i,j)=min(h[i],h[j])×(j−i) S(i,j)=min(h[i],h[j])×(j−i)
在每个状态下,无论长板或短板向中间移动一次,都会导致面积 底边宽度 −1 变短:
- 如果向内 移动短板 ,水槽的短板
min(h[i],h[j])
可能变大,下个水槽的面积 可能会增大 。 - 如果向内 移动长板 ,水槽的短板
min(h[i],h[j])
不变或变小,下个水槽的面积 一定会变小 。
所以,初始化双指针为左右两端,循环每轮将短板向内移动一次,并更新面积最大值,直到两指针相遇时跳出;即可获得最大面积。
算法流程:
- 初始化: 双指针
i
,j
分列水槽左右两端; - 循环收窄: 直至双指针相遇时跳出;
a. 更新面积最大值res
;
b. 选定两板高度中的短板,向中间收窄一格; - 返回值: 返回面积最大值
res
即可;
正确性证明:
若暴力枚举,水槽两板围成面积 S(i,j)
的状态总数为C(n,2)
。
假设状态 S(i,j)
下 h[i]<h[j]
,在向内移动短板至S(i+1,j)
,则相当于消去了 S(i,j−1),S(i,j−2),...,S(i,i+1)
状态集合。而所有消去状态的面积一定都小于当前面积(即<S(i,j)
),因为这些状态:
- 短板高度:相比
S(i,j)
相同或更短(即≤h[i]
); - 底边宽度:相比
S(i,j)
更短;
因此,每轮向内移动短板,所有消去的状态都 不会导致面积最大值丢失
复杂度分析:
时间复杂度O(N)
: 双指针遍历一次底边宽度N
。
空间复杂度O(1)
: 变量i
,j
,res
使用常数额外空间。
代码
#include <stdio.h> // 自定义max函数
int max(int a, int b) { return a > b ? a : b;
} // 函数的参数是整数数组和数组的长度
int maxArea(int* height, int heightSize) { int i = 0, j = heightSize - 1, res = 0; while(i < j) { res = (height[i] < height[j]) ? max(res, (j - i) * height[i++]): max(res, (j - i) * height[j--]); } return res;
} int main() { // 示例数组 int height[] = {1, 8, 6, 2, 5, 4, 8, 3, 7}; int heightSize = sizeof(height) / sizeof(height[0]); // 调用maxArea函数 int result = maxArea(height, heightSize); // 输出结果 printf("The maximum area is: %d\n", result); return 0;
}