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)了。

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