解法1:
对于每个位置能储存的水量是,取左面的最高档位,右面的最高档位之中较小的那个,再减去当前的档位即可,所以最直观的解法如下:
class Solution {
public int trap(int[] height) {
int sum = 0,left=0,right=0,now=1,maxl=0,maxr=0;
int n = height.length;
while(now<n-1)//开头和结尾的地方不会存水
{
left = now;
right = now;
maxl = now;
maxr=now;
while(left>=0)
{
if(height[left]>height[maxl])
maxl=left;
left--;
}
while(right<n)
{
if(height[right]>height[maxr])
maxr=right;
right++;
}
if( height[maxl]>height[now] && height[maxr]>height[now])
sum+=Math.min(height[maxl], height[maxr])-height[now];
now++;
}
return sum;
}
}
但是上面那种做法时间复杂度是O(N^2),时间主要消耗在找左右两个侧最大档位上,每次考虑当前容水量时都要去遍历查找一次,所以可以从这方面入手优化。
优化1:
可以先从左往右遍历一遍,记录在一个新数组中每个档位左面最大的档位,然后从右往左遍历一遍,记录在另一个数组中每个档位右面最大的档位,并且求出当前档位的存水量,最后将存水量累计起来即可。
class Solution {
public int trap(int[] height) {
int sum = 0,n=height.length;
if(n==0)return sum;
int[] leftnum = new int[n];
int[] rightnum = new int[n];
leftnum[0] = height[0];
rightnum[n-1] = height[n-1];
for(int i=1;i<n;i++)
{
if(height[i]>leftnum[i-1])
leftnum[i] = height[i];
else
leftnum[i] = leftnum[i-1];
}
for(int i=n-2;i>=0;i--)
{
if(height[i]>rightnum[i+1])
rightnum[i] = height[i];
else
rightnum[i] = rightnum[i+1];
sum+=Math.min(leftnum[i], rightnum[i])-height[i];
}
return sum;
}
}
上述代码进行了两次循环,时间复杂度是O(2*N)
优化2:可以找两个指针分别从数组的两端开始遍历,每次都取二者较小的那个,看看是否要更新leftmax或rightmax,不更新的话求当前的存水量,然后往中间移动较小的指针。之所以取较小的那个指针往中间移动,是因为较小的那个可能是存水的瓶颈,也就是中间可能有比较小的那个更大的档位,要尽快找到作为max,才能让中间的存水量能求出来。而求当前的存水量时,因为取了最小者,max也同时维护了,那就算中间有比max更大的档位,那当前的max也就是瓶颈了,时间复杂度是O(N)
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1, maxleft = 0, maxright = 0, res = 0;
while(left < right){
if(height[left] < height[right]){
if(height[left] > maxleft)
maxleft = height[left];
else
res += maxleft - height[left];
left++;
} else {
if(height[right] > maxright)
maxright = height[right];
else
res += maxright - height[right];
right--;
}
}
return res;
}
}
注:最后一段代码是copy Leetcode某位提交者的代码。