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

来源:互联网 发布:无标度网络 路径短 编辑:程序博客网 时间:2024/05/01 09:22

这题挺有意思。最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<len; i++){            maxLeft[i]=max; // [i]位置的大小取决于[i-1],那么当i==0时,就要给定一个初始值,就是0            max=Math.max(max, height[i]); // 然后通过height[i]更新maxLeft,为下一个位置[i+1]准备,这样设置循环就很完美        }                int[] maxRight = new int[len]; // 同理,再建一个array来村反maxRight        max=0;        for(int i=len-1; i>=0; i--){            maxRight[i]=max;            max=Math.max(max, height[i]);        }        int water=0;                for(int i=0; i<len; i++){            int diff=Math.min(maxLeft[i], maxRight[i])-height[i];            if(diff>0) 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<len; i++){            maxLeft[i]=max; // [i]位置的大小取决于[i-1],那么当i==0时,就要给定一个初始值,就是0            max=Math.max(max, height[i]); // 然后通过height[i]更新maxLeft,为下一个位置[i+1]准备,这样设置循环就很完美        }        int water=0;                int maxRight=0;        for(int i=len-1; 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<len; i++){            container[i]=maxLeft; // [i]位置的大小取决于[i-1],那么当i==0时,就要给定一个初始值,就是0            maxLeft=Math.max(maxLeft, height[i]); // 然后通过height[i]更新maxLeft,为下一个位置[i+1]准备,这样设置循环就很完美        }                int water=0;        int maxRight=0;        for(int i=len-1; 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;    }}


0 0
原创粉丝点击