經典水池接雨水算法

給定 n 個非負整數表示每個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。

在這裏插入圖片描述

上面是由數組 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度圖,在這種情況下,可以接 6 個單位的雨水(藍色部分表示雨水)。

示例:

輸入: [0,1,0,2,1,0,1,3,2,1,2,1]
輸出: 6
輸入: [2, 0, 2]
輸出: 2

在這裏插入圖片描述

首先,分析一下解題思路:

  • 方案一:按照一層一層統計
    要領逐層統計,每層去掉首尾空部分,然後計算中間缺失部分

第一層
取出藍色模塊,2個
在這裏插入圖片描述

第二層
取出藍色模塊,4個
在這裏插入圖片描述

第三層
取出藍色模塊,0個
在這裏插入圖片描述
代碼實現:

import java.util.*;

/**
 * @author yanghao
 * @version LeetCode42Test.java, v 0.1 2019-10-14 16:13
 */
public class LeetCode42Test {

    public static void main(String[] args){
        LeetCode42Test test = new LeetCode42Test();

        int[] height = new int[]{0,1,0,2,1,0,1,3,2,1,2,1};
        //int[] height = new int[]{2, 0, 2};
        System.out.println(test.leetCode42(height));
    }

    public int leetCode42(int[] height) {

        //取出最大值
        int max = 0;
        for(int h : height){
            if(h > max){
                max = h;
            }
        }

        int start = 0;
        int end = 0;
        int pool = 0;
        int all = 0;

        //一層一層計算
        for(int storey = 1;storey <= max;storey ++){

            for(int site = 0;site < height.length;site ++){

                if(height[site] >= storey){
                    //標記每層開始
                    if(start == 0){
                        start = site + 1;
                    }

                    //標記每層結束
                    end = site + 1;

                }

                if(height[site] < storey){
                    //標記池子數量
                    pool ++;
                }

            }

            //計算本層可裝水量 = 總池數 - 開頭池 - 結尾池
            all += pool - (start - 1) - (height.length - end);

            //每層初始化數據
            start = 0;
            end = 0;
            pool = 0;

        }

        return all;

    }

總結:
此方法可以實現最終的目的,而且代碼邏輯比較常規,但是循環次數多,如果給的數組足夠大和多,代碼效率將會很低下。


  • 方案二:自左向右統計
    要領1從左到右開始,如果右邊比左邊高,說明已經儲水(如下2種情況)
    計算公式 = 中間水位個數 * 最低水位 - 水底柱佔用空間(開頭0,1可以包含計算)
    在這裏插入圖片描述

在這裏插入圖片描述

要領2如果左邊比右邊高,情況有很多,可能就是無法儲水了,或者存在更低的儲水池(如下3種情況)
如果把中間最高位分割之後,右邊全部倒置,即可和左邊一致計算公式
在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

代碼實現:

import java.util.*;

/**
 * @author yanghao
 * @version LeetCode42Test.java, v 0.1 2019-10-11 19:13
 */
public class LeetCode42Test {

    public static void main(String[] args){
        LeetCode42Test test = new LeetCode42Test();

        int[] height = new int[]{0,1,0,2,1,0,1,3,2,1,2,1};
        //int[] height = new int[]{2, 0, 2};
        System.out.println(test.leetCode42(height));
    }

    public int leetCode42(int[] height) {

        //從最高峯,分成2個數組
        int max = 0;
        int index = 0;

        for(int i = 0; i < height.length; i ++){
            if(height[i] > max){
                max = height[i];
                index = i;
            }
        }

        int[] left = new int[index + 1];
        System.arraycopy(height, 0, left, 0, index + 1);

        int[] right = new int[height.length - index];
        System.arraycopy(height, index, right, 0, height.length - index);
        //倒置
        for(int i = 0;i < right.length/2; i ++){
            int temp = right[i];
            right[i] = right[right.length - 1 -i];
            right[right.length - 1 -i] = temp;
        }

        //左邊 + 右邊
        return getPool(left) + getPool(right);

    }

    private int getPool(int[] height) {
        //記錄左邊位置
        int left = 0;
        //記錄實心部分
        int temp = 0;
        //記錄總
        int all = 0;

        for(int site = 1; site < height.length; site ++){

            if(height[site] >= height[left]){
                //右邊比左邊高,記錄池水 = 長(剔除兩邊牆) * 寬 - 實心
                all += (site - left - 1) * height[left] - temp;

                //左邊位置重新標記爲右邊位置
                left = site;

                //實心數據初始化
                temp = 0;

            }else{
                //左邊比右邊高,記錄實心數據
                temp += height[site];

            }

        }

        return all;

    }
}

總結:
此方法效率高,代碼執行速度很快,但是理解成本較高,邏輯複雜。

大家可以發揮自己的想象,應該還有更多方式可以寫出來~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章