42 Trapping Rain Water 【基本解法和优化方法】

这题挺有意思。最naive的想法就是看每个位置左右两边的bar的高低,然后来决定当前位置能装多少水。有意思的地方在于,每个位置的组左右高度,不是其相邻位置的高度,而是整个左边部分和右边部分的最大值!这是一个最关键的规律,发现了规律,就可以顺利解题了,剩下的事情就是用什么算法来实现,算法的效率复杂度的区别了。

最直接的方法,遍历每一个元素,在扫一遍左右两边,找到左右最大值,然后取较小值,就找到了threshold,然后比较本身和threshold的大小,本身大,则无雨水积累,小则取差值。那么一个n个点,每个点又遍历一次array,有n次操作,所以复杂度是N平方。具体代码就不写了。


基于上面的思路,想想优化的方法。其实每个元素的左边的最大值,可以直接用到下一个元素的最大值,通过比较maxLeft[i] 和 height[i] 就可以得到了,完全没有必要再遍历一遍。所以通过新建一个array 来记录每一个位置的maxLeft就可以了。

同样的,可以再建立一个array从右往左找到maxRright,然后把两个array读一遍,去min得到threshold,再比较和当前的height的高低,计算得到每个位置的水位。复杂度是3N,因为扫了3次array,测试后的效率为67%

public class Solution {
    public int trap(int[] height) {
        int len = height.length;
        
        int[] maxLeft = new int[len]; // 建一个array来存放maxLeft
        int max=0; // 这里设置成0似乎和max的意思不一致,实际上是一致的,要准确把握题意,Y轴不算是一个bar,也就是说如果第一个位置是0,那么左边就一直不会有积水,看题中的图就理解。
        for(int i=0; i=0; i--){
            maxRight[i]=max;
            max=Math.max(max, height[i]);
        }

        int water=0;        
        for(int i=0; i0) water+=diff;
        }
        
        return water;
    }
}


上面的方法其实还可以再优化,那就是在从右往左的过程中,其实就可以计算出累加的水的部分了,因为所有的maxLeft都ready了,每得到一个maxRight就可以得到threshold然后得到累加的水位。复杂度是2N,效率有所提升,77%

public class Solution {
    public int trap(int[] height) {
        int len = height.length;
        
        int[] maxLeft = new int[len]; // 建一个array来存放maxLeft
        int max=0; // 这里设置成0似乎和max的意思不一致,实际上是一致的,要准确把握题意,Y轴不算是一个bar,也就是说如果第一个位置是0,那么左边就一直不会有积水,看题中的图就理解。
        for(int i=0; i=0; i--){
            int diff=Math.min(maxLeft[i], maxRight)-height[i];
            if(diff>0) water+=diff;
            maxRight=Math.max(maxRight, height[i]);
        }
        return water;
    }
}



看了看别人的解法,跟我的思路也类似,区别在于,在从右往左的过程中,他没有new出新的variable,只是都存放在原有的container空间里,效率有90%,代码如下:

public class Solution {
    public int trap(int[] height) {
        int len = height.length;
        int[] container = new int[height.length];
        
        int maxLeft=0; // 这里设置成0似乎和max的意思不一致,实际上是一致的,要准确把握题意,Y轴不算是一个bar,也就是说如果第一个位置是0,那么左边就一直不会有积水,看题中的图就理解。
        for(int i=0; i=0; i--){
            container[i]=Math.min(maxRight, container[i]); // 这里container记录的不是右边的最大值,而是:左右最大值中的较小值,就是为了找到关键的threshold。
            // 而右边的最大值只需要通过一个参数来记录即可,因为scan过这一遍后,就得到结果了,没必要保存每个位置的右最大值。如下;
            maxRight=Math.max(maxRight, height[i]);  // 当前的height[i]是为了下一个位置[i-1]的更新做准备
            if(container[i]>height[i]) water+=container[i]-height[i];
        }
        return water;
    }
}


你可能感兴趣的:(Uber实习第一面准备)