题目难度: Hard
原题描述:
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1]
, return 6
.
题目大意:
有n条长木块,每条木块的宽度均为1,高度为题目给出的数组中的非负整数。问在这些木块组成的图形之中加水,最多能加多大体积的水?
解题思路:
一开始对着题目给出的图来想,想错了,以为可以把题目的输入划分成一个个“U”型的部分,即每一部分都是先下降后上升,然后再求每一部分能装水的体积,最后加起来。但是这种方法是不行的,如果有多个“U”型连起来且最左和最右都是最高的木块,这样能装水的体积就不止这么少了。
后来再想才找到了正确的算法。每一次在数组中找两个最大的数,如果有多个相同,则任取一个。假设左边的数是a,右边的数是b,则在a和b之间的数就可以调用getPartWater()函数来计算在这两个数之间能装水的最大值。接着分别对a左边的数和b右边的数递归调用本函数,来获得这两部分能装水的最大值,最后把这三部分能装水的最大值加起来,就是问题的答案。
其中getPartWater()函数是用来计算两个木块之间能装水的最大值。其主要思想是先求最左和最右木块高度的最小值minHeight,然后用minHeight来填充在这两条木块之间的空隙,用总的面积减去在填充区域之间实际木块的面积,就是能装水的最大值。
时间复杂度分析:
时间复杂度似乎比较难分析?
以下是代码:
public class Solution {
private int getPartWater(int i , int j , int[] height)
{
int barSum = 0;
int minHeight = Math.min(height[i], height[j]);
for(int k=i ; k<=j ; ++k){
barSum += Math.min(height[k], minHeight);
}
int ans = (j-i+1)*minHeight - barSum;
return Math.max(ans, 0);
}
private int getAns(int[] height , int left , int right)
{
int ans = 0;
int maxValue1 = -1 , maxValue2 = -1;
int maxIndex1 = 0 , maxIndex2 = 0;
for(int i=left ; i<=right ; ++i){
if(height[i]>maxValue1){
maxValue1 = height[i];
maxIndex1 = i;
}
}
for(int i=left ; i<=right ; ++i){
if(height[i]>maxValue2 && i!=maxIndex1){
maxValue2 = height[i];
maxIndex2 = i;
}
}
int leftIndex = Math.min(maxIndex1, maxIndex2);
int rightIndex = Math.max(maxIndex1, maxIndex2);
if(leftIndex < rightIndex){
ans += getPartWater(leftIndex, rightIndex, height);
}
if(leftIndex > left){
ans += getAns(height, left, leftIndex);
}
if(rightIndex < right){
ans += getAns(height, rightIndex, right);
}
return ans;
}
public int trap(int[] height)
{
return getAns(height, 0, height.length-1);
}
}