LeetCode——接雨水問題


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

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

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

A:
(引用自《labuladong的算法小抄》)
位置 i 能達到的⽔柱⾼度和其左邊的最⾼柱⼦、右邊的最⾼柱⼦有關,我們分別稱這兩個柱⼦⾼度為 l_max和 r_max ;位置 i 最⼤的⽔柱⾼度就是 min(l_max, r_max) 。
用一個備忘錄記錄一下:

    public int trap(int[] height) {
        if (height.length <= 2)
            return 0;
        int n = height.length;
        int[] l_max = new int[n];
        l_max[0] = height[0];
        int[] r_max = new int[n];
        r_max[n - 1] = height[n - 1];
        //左側最高
        for (int i = 1; i < n; i++) {
            l_max[i] = Math.max(l_max[i - 1], height[i]);
        }
        //右側最高
        for (int i = n - 2; i >= 0; i--) {
            r_max[i] = Math.max(r_max[i + 1], height[i]);
        }
        int sum = 0;
        for (int i = 1; i < n - 1; i++) {
            sum += Math.min(l_max[i], r_max[i]) - height[i];
        }
        return sum;
    }

使用雙指針法:

    public int trap(int[] height) {
        if (height.length <= 2)
            return 0;
        int n = height.length;
        int left = 0, right = n - 1;
        int l_max = height[0], r_max = height[n - 1];
        int sum = 0;
        while (left <= right) {
            l_max = Math.max(l_max, height[left]);
            r_max = Math.max(r_max, height[right]);
            if (l_max > r_max) {
                sum += r_max - height[right];
                right--;
            } else {
                sum += l_max - height[left];
                left++;
            }
        }
        return sum;
    }

很容易理解, l_max 是 height[0..left] 中最⾼柱⼦的⾼度, r_max 是 height[right..end] 的最⾼柱⼦的⾼度。此時的 l_max 是 left 指針左邊的最⾼柱⼦,但是 r_max 並不⼀定是left 指針右邊最⾼的柱⼦,這真的可以得到正確答案嗎?
其實這個問題要這么思考,我們只在乎 min(l_max, r_max) 。對於上圖的情況,我們已經知道 l_max < r_max 了,⾄於這個 r_max 是不是右邊最⼤的,不重要,重要的是 height[i] 能夠裝的⽔只和 l_max 有關。

Q:給你一個 m x n 的矩陣,其中的值均為正整數,代表二維高度圖每個單元的高度,請計算圖中形狀最多能接多少體積的雨水。

示例:
給出如下 3x6 的高度圖:
[
[1,4,3,1,3,2],
[3,2,1,3,2,4],
[2,3,3,2,3,1]
]
返回 4 。

A:引用:https://blog.csdn.net/qq_29996285/article/details/86696301
思路:

  • 搜索隊列使用優先級隊列(STL的優先隊列的底層實現默認是大頂堆),越低的點優先級越高(構造小頂堆),優先級越高的點越優先搜索。
  • 以矩陣四周的點作為起始點進行廣度優先搜索(這些點在搜索前需要push進隊列中)。
  • 使用一個二維數組對push進隊列的點進行標記,如果該點被標記過,則不會被push到隊列中。
  • 只要優先級隊列不空,即去除優先級隊列的隊頭元素進行搜索,按照上下左右四個方向進行擴展,擴展過程中忽略超出邊界的點和已經入隊的點。
  • 當某個點 (x, y, h) 進行拓展時,得到的新點 (newx, newy) 的高度 height 小於 h,則——積水高度+=h-height;,並更新 h 的值為 height 。
  • 將(newx, newy, height)入隊,並做上標記。

舉例:

將四周的點添加至優先隊列Q中,並做上標記

開始進行擴展:
藍色代表正在搜索的節點,綠色代表隊中的節點,紫色代表已完成節點,紅色代表擴展的新節點(優先級隊列中的元素:優先隊列<行,列,高>)

代碼:

public int trapRainWater(int[][] heightMap) {
        if (heightMap.length <= 2 || heightMap[0].length <= 2)
            return 0;
        int n = heightMap.length;
        int m = heightMap[0].length;
        boolean[][] visit = new boolean[n][m];
        PriorityQueue<int[]> pq = new PriorityQueue<>();
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (i == 0 || i == n - 1 || j == 0 || j == m - 1) {
                    pq.offer(new int[]{i, j, heightMap[i][j]});
                    visit[i][j] = true;
                }
            }
        }
        int res = 0;
        int[] dirs = {-1, 0, 1, 0, -1};
        while (!pq.isEmpty()) {
            //小跟堆的最小高度
            int[] poll = pq.poll();
            for (int k = 0; k < 4; k++) {
                int newx = poll[0] + dirs[k];
                int newy = poll[1] + dirs[k + 1];
                if (newx >= 0 && newy >= 0 && newx < n && newy < m && !visit[newx][newy]) {
                    //比當前這個最小高度還小,說明里面可以裝水
                    if (poll[2] > heightMap[newx][newy])
                        res += (poll[2] - heightMap[newx][newy]);
                    //放入堆
                    pq.offer(new int[]{newx, newy, Math.max(heightMap[newx][newy], poll[2])});
                    visit[newx][newy] = true;
                }
            }
        }
        return res;
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM