LeetCode-[数组]-接雨水(三种解法)

题目描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

1. 暴力解法

    O(N^2) 遍历数组,找出 i 左右两边的最大值,计算出 i 位置能存的水
public int trap(int[] height) {
          
   
        if (height == null || height.length < 3) {
          
   
            return 0;
        }
        int res = 0;
        for (int i = 1; i < height.length - 1; i++) {
          
   
            int leftMax = 0, rightMax = 0;  //定义在for里,因为每次都要更新
            for (int j = 0; j < i; j++) {
          
      //i 左边最大值
                leftMax = Math.max(leftMax, height[j]);
            }
            for (int j = height.length - 1; j > i; j--) {
          
     //i 右边最大值
                rightMax = Math.max(rightMax, height[j]);
            }
            //与0比较:负数不要,说明i位置的柱子比左右两边都突出
            res += Math.max(0, Math.min(leftMax, rightMax) - height[i]);
        }
        return res;
    }

2. 帮助数组

    时复、空复:O(N) 对数组进行预处理,把数组i每个位置的左右两边的最大值都记录下来(从左往右和从右往左) 这样就剩下暴力法中每次都要遍历找最大值的功夫
public int trap(int[] height) {
          
   
        if (height == null || height.length < 3) {
          
   
            return 0;
        }
        int res = 0;
        int n = height.length - 2;  //头尾不用比较
        int[] leftMaxs = new int[n];
        int[] rightMaxs = new int[n];
        leftMaxs[0] = height[0];
        rightMaxs[n - 1] = height[n + 1];

        for (int i = 1; i < n; i++) {
          
     //生成 左——>右 的最大值数组
            leftMaxs[i] = Math.max(leftMaxs[i - 1], height[i]);
        }
        for (int i = n - 2; i >= 0; i--) {
          
      //生成 右——>左 的最大值数组。下标比较绕,但可以想明白
            rightMaxs[i] = Math.max(rightMaxs[i + 1], height[i + 2]);
        }
        for (int i = 1; i <= n; i++) {
          
     //遍历数组,帮助数组可以方便的确定 i 位置左右两边的最大值
            res += Math.max(0, Math.min(leftMaxs[i - 1], rightMaxs[i - 1]) - height[i]);
        }
        return res;
    }

3. 最优解——双指针

    时间复杂度:O(N) 空间复杂度:O(1) 两个帮助数组可以用双指针+双变量优化,leftMax、rightMax分别记录 l 左右两边的最大值 在 l、r 遍历过程中,遇到更大值就更新 leftMax、rightMax
public int trap(int[] height) {
          
   
        if (height == null || height.length < 3) {
          
   
            return 0;
        }
        int res = 0;
        int l = 1, r = height.length - 2;  //双指针
        int leftMax = height[0], rightMax = height[height.length - 1];   //双变量
        while (l <= r) {
          
   
            if (leftMax <= rightMax) {
          
   
                res += Math.max(0, leftMax - height[l]);
                leftMax = Math.max(leftMax, height[l++]); //更新左边的最大值
            } else {
          
   
                res += Math.max(0, rightMax - height[r]);
                rightMax = Math.max(rightMax, height[r--]); //更新右边的最大值
            }
        }
        return res;
    }
经验分享 程序员 微信小程序 职场和发展