leetcode 42.接雨水——雙指針

題目鏈接 傳送門

解析

我承認一開始想複雜了,一看答案發現自己是個沙雕。
我一開始居然往極值和差分那個方向去想了,還有就是題目居然標的是困難,唉。
官方題解有四種方案

  1. 暴力
  2. 動態規劃
  3. 雙指針。

前三種,看官方題解就行,屬於一點就通的方法。
只是第四種方法即雙指針法我不是很熟悉,所以覺得有點嚼頭。
我並沒有死扣官方的代碼,而是在題解點出的方向上自己重構的代碼。
思路如下

首先,在寫動態規劃的時候,我們就發現了幾個特點。
第一: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)了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章