问题:
给定一个数组,想象成一个桶。问最多能装多少水?
例【1,5,3,6】 最多装2格水
O
O ~ O
O ~ O
O O O
O O O
O O O O
解题思路:
我们把每一列当成一块板,根据分析,第一块板和最后一块板一定不能蓄水,所以问题变成了所有板所能蓄水最大值的总和。先明确这个思路,之后再想办法。
那么如何确定当前板可以蓄多少水呢?和它左右两边最长的板有关系。等于左右两边最长板的较小者减去当前板的长度和0取最大值。
表示为:
当前板蓄水量 = math.max(0, math.min(左最长板,右最长板) - 当前)
第一、最后一块板蓄水量 = 0
方法一:O(n^2)
简单暴力,挨个遍历每一块板,求出左右最大值,根据上面公式进行计算。不做演示
方法二:O(n)
根据方法一可知,时间复杂度主要在寻找每一块板的左右最大值。那么优化的方向就是使用O(n)复杂度获得每一块板的左右最大值。
怎么做呢?借助辅助数组。
例如【1,5,3,6】,则左边最大值数组为【1,5,5,6】,右边最大值数组为【6,6,6,6】
三个数组分别表示当前板长度,当前板的左边板最大长度、当前板的右边板最大长度。
public static int shui(int[] param) {
if(param == null || param.length < 2) return 0;
int[] L = new int[param.length];
int[] R = new int[param.length];
for(int i=1;i<param.length;i++) {
L[i] = Math.max(param[i], param[i - 1]);
}
for(int i=param.length - 2;i>0;i--) {
R[i] = Math.max(param[i], param[i + 1]);
}
int shui = 0;
for(int i = 1; i<param.length-1;i++)
{
shui += Math.max(0, Math.min(L[i], R[i]) - param[i]);
}
return shui;
}
方法三:O(n)
方法二空间复杂度太大,所以还有优化空间。
可以从两边同时进行判断,并且保存当前左右最大值。
如果左边最大值小于右边最大值,那么左边的当前板所能蓄水量就可确定 = math.max(0, 左最大值 - 当前)。并且当前索引++,标记左边最大值 = math.max(左最大值,当前板)。
如果右边最大值小于左边最大值,那么右边的当前板所能蓄水量就可确定 = math.max(0, 右最大值 - 当前)。并且当前索引--,标记右边最大值 = math.max(右最大值,当前板)。
如果相等,随便归属一边,或两边一起计算。
/*
* 给定一个数组,想象成一个桶。问最多能装多少水
* 例【1,5,3,6】 最多装2格水
* O
* O ~ O
* O ~ O
* O O O
* O O O
* O O O O
*
*/
public static int shui(int[] param) {
if(param == null || param.length < 2) return 0;
int L = 1, R = param.length-2, shui = 0;
int lmax = param[0], rmax = param[R + 1];
while(L<=R) {
if(lmax <= rmax) {
shui += Math.max(0, lmax - param[L]);
lmax = Math.max(lmax, param[L++]);
}else {
shui += Math.max(0, rmax - param[R]);
rmax = Math.max(rmax, param[R--]);
}
}
return shui;
}
public static void main(String[] args) {
int[] a = {2,1,3,5,2,6};
System.out.println(shui(a));
}