接雨水

题目描述
  • 题目来自LeetCode接雨水_第1张图片
    接雨水_第2张图片

解答

  • 双指针方法(动态规划方法具体请查看@windliang 这里)
    • 基本思想:基于列的计算,即计算所有列的可蓄水值,求和即可;

    • 说一下动态规划:

      • 每一列的蓄水情况,取决于自身高度,以及左边列最大高度和右边列最大高度(左右最大高度);
      • 举例来说,如下图,红色箭头列 C 3 C_3 C3自身高度2,左右列最大高度分别是1、3,根据木桶理论,左边列最大高度为1,决定了 C 3 C_3 C3可蓄水值;而 C 3 C_3 C3本身高度2,比1大,因此 C 3 C_3 C3列事实上无法蓄水;绿色箭头列 C 4 C_4 C4道理是一样的, C 4 C_4 C4可蓄水1个单位;
      • 可以发现需要计算每一列的左右最大高度;一种方法是左右各遍历一次,计算每一列的左右最大高度;可以使用动态规划(如 C 4 L C_{4L} C4L = MAX( C 3 C_3 C3, C 3 L C_{3L} C3L), C 4 L C_{4L} C4L C 4 C_4 C4列的左边最大高度,不含 C 4 C_4 C4),也可以使用普通遍历;接雨水_第3张图片
    • 进阶版-双指针方法

      • 先上代码(先用@windliang):
      public int trap(int[] height) {
               
          int sum = 0;
          int max_left = 0;
          int max_right = 0;
          int left = 1;
          int right = height.length - 2; // 加右指针进去
          for (int i = 1; i < height.length - 1; i++) {
               
              //从左到右更
              if (height[left - 1] < height[right + 1]) {
               
                  max_left = Math.max(max_left, height[left - 1]);
                  int min = max_left;
                  if (min > height[left]) {
               
                      sum = sum + (min - height[left]);
                  }
                  left++;
              //从右到左更
              } else {
               
                  max_right = Math.max(max_right, height[right + 1]);
                  int min = max_right;
                  if (min > height[right]) {
               
                      sum = sum + (min - height[right]);
                  }
                  right--;
              }
          }
          return sum;
      }
      
      作者:windliang
      链接:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
      来源:力扣(LeetCode)
      著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
      
      • 假设长度序列长度n=12([0,1,0,2,1,0,1,3,2,1,2,1]),仍是基于列的方法
      • 上一个方法是,如果计算列 C 6 C_6 C6的蓄水情况时,需要提前计算 C 6 C_6 C6左右的高度情况,那么有没有什么方法可以边遍历列边确定当前列的左右高度情况(左最大高度:0–> C 5 C_{5} C5的最大高度;右历史: C 7 C_{7} C7–> C 11 C_{11} C11的最大高度);
      • 先考虑一些特殊的列看看, C 0 C_0 C0 C 11 C_{11} C11列不用考虑,最中间的列依赖于暂时未知的左右高度情况,所以可以先考虑 C 1 C_1 C1或者 C 10 C_{10} C10
      • 对于 C 1 C_1 C1或者 C 10 C_{10} C10,各自有一边只有一个列( C 0 C_0 C0 C 9 C_{9} C9);现在比较两者,有两种情况:
        • 情况1 C 0 ≤ C 11 C_0 \le C_{11} C0C11,那么对于 C 1 C_1 C1,显然其能否蓄水,只取决于 C 1 C_1 C1左最大高度 是否比 C 1 C_1 C1更大,而 C 1 C_1 C1左边只有一个 C 0 C_0 C0
        • 如上 C 4 L , C_{4L}, C4L定义 C x C_x Cx的左最大高度为 C x L C_{xL} CxL = max( C x − 1 C_{x-1} Cx1, C ( x − 1 ) L C_{(x-1)L} C(x1)L),可以看到这是一个要迭代更新的值; 定 义 定义 C_x 的 右 最 大 高 度 为 的右最大高度为 C_{xR}$ = max( C x + 1 C_{x+1} Cx+1, C ( x + 1 ) R C_{(x+1)R} C(x+1)R)
        • 可以知道 C 1 L C_{1L} C1L=max( C 0 C_0 C0, C 0 L C_{0L} C0L)= C 0 C_0 C0=0,0小于 C 1 C_1 C1=1,由此 C 1 C_1 C1是无法蓄水的;参考下图例子
        • 情况2 C 0 > C 11 C_0 \gt C_{11} C0>C11,那么对于 C 10 C_{10} C10,显然其能否蓄水,只取决于 C 10 C_{10} C10右最大高度 是否比 C 10 C_{10} C10更大,而 C 10 C_{10} C10右边只有一个 C 11 C_{11} C11,流程和上一个情况一样;当然对于本例,该情况不满足;接雨水_第4张图片
      • 上述 C 1 C_1 C1处理后,可以继续查看 C 2 C_2 C2,同时更新 C 2 L = m a x ( C 1 , C 1 L ) C_{2L}=max(C_1, C_{1L}) C2L=max(C1,C1L) C 1 L C_{1L} C1L是已知的;需要注意的是,并不是说可以继续计算 C 2 C_2 C2,因为 C 2 C_2 C2左边引入 C 1 C_1 C1,如果 C 1 C_1 C1> C 11 C_{11} C11,则 C 2 C_2 C2的蓄水情况就取决于 C 2 C_2 C2的右最大高度 C 2 R C_{2R} C2R了,而 C 2 R C_{2R} C2R并不清楚;
      • 换个思路, C 1 C_1 C1> C 11 C_{11} C11,所以 C 10 C_{10} C10的蓄水情况取决于 C 10 C_{10} C10的右最大高度 C 10 R C_{10R} C10R,而 C 10 R C_{10R} C10R又很好算,这就是从左右两边开始计算的好处;此时发生列交替,即我们去计算 C 10 R C_{10R} C10R,相应计算类似 C 1 C_{1} C1的计算; C 10 C_{10} C10算完后,可以考虑 C 9 C_{9} C9,考虑的方法和 C 2 C_{2} C2一样,整个过程可以如此迭代或者交替进行
    • 小结:该方法的两个最大特点

      • 从边界开始计算,则左右最大高度很好计算
      • 利用一个特点:某一列C的一边side_A 存在高度大于该列另一边side_B的所有高度,则C列的蓄水情况取决于side_B;该性质也依赖于上一个特点;每一次列交替都发生于出现了一列当前已知的最大高度列;
      • 时间复杂度是 O ( n ) O(n) O(n),空间是 O ( 1 ) O(1) O(1)
  • 基于栈的方法-待续

你可能感兴趣的:(技术问题,#,算法,算法,动态规划,leetcode)