算法-雨水问题

2018/09/05 算法 Java 雨水
  本文为「原创」内容,如需转载请注明出处!             
本文共 3547 字,需 44 分钟阅读

背景

  1. 给定一个一维数组,求出两条边界的最大储水量值,Container With Most Water

rain-trap

    Input: [1,8,6,2,5,4,8,3,7]
    Output: 49
  1. 给定一个一维数组代表边界高度求最大的储水量,Trapping Rain Water

rain-trap

    Input: [0,1,0,2,1,0,1,3,2,1,2,1]
    Output: 6
  1. 给定一个 mxn 的二维数组,求出其代表的容器的最大储水量,Trapping Rain Water II

rain-trap

rain-trap

    Given the following 3x6 height map:
    [
        [1,4,3,1,3,2],
        [3,2,1,3,2,4],
        [2,3,3,2,3,1]
    ]

    Return 4.

算法

两条边界储水量

该题可用双指针法求解,每次保留高度较高的那边,计算的时候按短边计算,详情参考 Solution

    public int maxArea(int[] height) {
        if(height == null || height.length < 2){
            return 0;
        }
        int ans =0,l=0,r=height.length-1;
        while (l<r){
            // 短边法,找到 i,j 的储水量,更新最大值
            ans  = Math.max(ans,Math.min(height[l],height[r]) * (r-l));
            // 保留长边,下次迭代
            if(height[r] > height[l]){
                l++;
            }else{
                r--;
            }
        }
        return ans;
    }

一维数组储水量

该题用动态规划比较好理解,详情参考 Solution

    public int trap2(int[] heights){
        if(heights == null || heights.length == 0){
            return 0;
        }
        int[] leftMax  = new int[heights.length];
        int[] rightMax = new int[heights.length];
        leftMax[0] = heights[0];
        rightMax[heights.length-1] = heights[heights.length-1];
        for(int i=1;i<heights.length;i++){
            // i 左边的最大边界
           leftMax[i] = Math.max(leftMax[i-1],heights[i]);
        }
        for(int j=heights.length-2;j>=0;j--){
            // j 右边的最大边界
            rightMax[j] = Math.max(rightMax[j+1],heights[j]);
        }
        int ans = 0;
        for (int i = 0; i < heights.length; i++) {
           // 用至该边界能储水的最大边界,减去该边界占的空间,剩下的就是能够储水的空间
           ans += Math.min(leftMax[i],rightMax[i]) - heights[i];
        }
        return ans;
    }

二维数组储水量

该题用优先队列,详情参考 Solution

public class Solution {
     int[][] next = { {1, 0}, {0, 1}, {-1, 0}, {0, -1}};

    public int trapRainWater(int[][] heights) {
        if (heights == null || heights.length == 0 || heights[0].length == 0) {
            return 0;
        }
        boolean[][] mark = new boolean[heights.length][heights[0].length];
        // int[],有三个值 i,j,高度
        // o1[2]-o2[2] 高度作为比较维度
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[2] - o2[2];
            }
        });

        // 先把最外层圈放入队列
        for (int i = 0; i < heights.length; i++) {
            for (int j = 0; j < heights[i].length; j++) {
                if (i == 0 || j == 0 || i == heights.length - 1 
                    || j == heights[i].length - 1) {
                    queue.add(new int[]{i, j, heights[i][j]});
                    mark[i][j]=true;
                }
            }
        }

        int sum = 0;
        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            for (int k = 0; k < next.length; k++) {
                int ti = cell[0] + next[k][0];
                int tj = cell[1] + next[k][1];
                if (ti < 0 || tj < 0 || ti >= heights.length
                     || tj >= heights[0].length || mark[ti][tj]) {
                    continue;
                }
                // cell 是当前外围的最低点
                // 由于是由外向内遍历的,所以当前点为墙,ti,tj 指向的是内部
                // 计算储水量,然后累加
                sum += Math.max(0, cell[2] - heights[ti][tj]);
                mark[ti][tj] = true;
                // 因为要缩小一圈围墙,所以要取较大值,才能顶掉里面的值
                queue.add(new int[]{ti, tj, Math.max(cell[2], heights[ti][tj])});
            }
        }
        return sum;
    }
}

搜索

    文章目录