題目鏈接 傳送門
解析
我承認一開始想複雜了,一看答案發現自己是個沙雕。
我一開始居然往極值和差分那個方向去想了,還有就是題目居然標的是困難,唉。
官方題解有四種方案
- 暴力
- 動態規劃
- 棧
- 雙指針。
前三種,看官方題解就行,屬於一點就通的方法。
只是第四種方法即雙指針法我不是很熟悉,所以覺得有點嚼頭。
我並沒有死扣官方的代碼,而是在題解點出的方向上自己重構的代碼。
思路如下
首先,在寫動態規劃的時候,我們就發現了幾個特點。
第一:left_max[i]>right_max[i]的時候,i點的雨水高度取決於left_max[i],反過來則取決於right_max[i].
第二:left_max[]從左往右總是單調遞增的(left_max[i+1]>=left_max[i]),right_max[]從右往左也是單調遞增的(right_max[i-1]>=right_max[i])。
所以如果left_max[i]>right_max[j] (i<j)
時
必然有left_max[j]>right_max[j]
,則此時可以確定j點的雨水高度取right_max[j]。
反之可以確定i點雨水高度取left_max[i]。
所以我們就可以不要left_max[]與right_max[]這兩個數組了,取而代之的是代表left_max[i] 的變量left_max,和代表right_max[j] 的變量right_max。
設置l,r分別爲0,size-1,left_max和right_max分別初始化爲height[l],height[r]。
然後如果left_max>right_max,則r點雨點高度確定,計算r點後,r--左滑一格,並更新right_max
反之則l點雨水高度確定,計算l點後l++右滑一格,更新left_max
循環直至l與r相遇。
代碼(附帶有暴力,動規,雙指針三種解法)
class Solution {
public int trap(int[] height) {
// return baoLi(height);
// return dongGui(height);
return doublePoints(height);
}
public int min(int a,int b){
if(a>b)return b;
else return a;
}
//暴力
public int baoLi(int[] height){
int ans=0;
for(int i=0;i<height.length;i++){
int left_max=0,right_max=0;
for(int j=0;j<=i;j++)if(left_max<height[j])left_max=height[j];
for(int j=i;j<height.length;j++)
if(right_max<height[j])right_max=height[j];
ans+=min(right_max,left_max)-height[i];
}
return ans;
}
//動態規劃
public int dongGui(int[] a){
if(a==null||a.length==0)return 0;
int ans=0,n=a.length;
int left[]=new int[a.length];
int right[]=new int[a.length];
left[0]=a[0];
for(int i=1;i<n;i++){
if(a[i]>left[i-1])left[i]=a[i];
else left[i]=left[i-1];
}
right[n-1]=a[n-1];
for(int i=n-2;i>=0;i--){
if(a[i]>right[i+1])right[i]=a[i];
else right[i]=right[i+1];
}
for(int i=0;i<n;i++){
ans+=min(right[i],left[i])-a[i];
}
return ans;
}
//雙指針
public int doublePoints(int[] a){
if(a==null||a.length==0)return 0;
int ans=0;
int l=0,r=a.length-1;
int left_max=a[l],right_max=a[r];
while(l<r){
if(left_max<right_max){
ans+=left_max-a[l];
l++;
left_max=left_max>a[l]?left_max:a[l];
}
else{
ans+=right_max-a[r];
r--;
right_max=right_max>a[r]?right_max:a[r];
}
}
return ans;
}
}
一點感悟
覺得很多算法都是一點一點改進的,就拿這道題來說,剛開始打死也想不到雙指針這樣的解法。但是先暴力,優化重複計算問題後就有了動態規劃將時間複雜度降到o(n),又通過分析了左右數組的關係來想到用雙指針,空間複雜度也降到o(1)了。