1. 程式人生 > 其它 >LeetCode——42. 接雨水(Java)

LeetCode——42. 接雨水(Java)

題目描述

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

示例 1:
輸入:height = [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:
輸入:height = [4,2,0,3,2,5]
輸出:9

題目描述

返回能接到的雨水格子數,官方題解給出了很多種方法,說幾個可以理解的方法

任何問題離不開暴力解答,但是這道題的暴力思想我估計都不好想

你需要知道如何計算節水數量的方式,就是當前元素到左右最高邊界中較小的差然後求和

所以我們需要先計算出每個元素盛水的左右邊界值,這裡就有兩種方式

暴力的話就需要每次計算左右邊界,如果採用動態規劃的思想,可以想把邊界值儲存起來

這就是兩種方法,再其次就是採用單調遞減棧的方式,因為只要高度是遞減的

那當它一旦遇到遞增的就可以接水,利用這個思路,我們可以使用遞減棧的思想來計算

最後還有一種雙指標的方式,因為時間有限就沒有理解,大家可以去官方題解檢視解析或者視訊

正確程式碼

    // 暴力
    public int trap(int[] height) {
        int ans = 0;
        int size = height.length;

        for (int i = 1; i < size - 1; i++) {
            int max_left = 0, max_right = 0;
            for (int j = i; j >= 0; j--) {
                max_left = Math.max(max_left, height[j]);
            }
            for (int j = i; j < size; j++) {
                max_right = Math.max(max_right, height[j]);
            }
            ans += Math.min(max_left, max_right) - height[i];
        }
        return ans;
    }

    // 動態規劃
    public int trap01(int[] height) {
        if (height == null || height.length == 0) {
            return 0;
        }

        int ans = 0, size = height.length;
        int[] left_max = new int[size];
        int[] right_max = new int[size];

        left_max[0] = height[0];
        for (int i = 1; i < size; i++) {
            left_max[i] = Math.max(height[i], left_max[i - 1]);
        }

        right_max[size - 1] = height[size - 1];
        for (int i = size - 2; i >= 0; i--) {
            right_max[i] = Math.max(height[i], right_max[i + 1]);
        }

        for (int i = 1; i < size - 1; i++) {
            ans += Math.min(left_max[i], right_max[i]) - height[i];
        }
        return ans;
    }

    // 單調遞減棧
    public int trap02(int[] height) {
        int ans = 0, current = 0;
        Deque<Integer> stack = new LinkedList<>();
        while (current < height.length) {
            while (!stack.isEmpty() && height[current] > height[stack.peek()]) {
                int top = stack.pop();
                if (stack.isEmpty()) break;
                int distance = current - stack.peek() - 1;
                int bounded_height = Math.min(height[current], height[stack.peek()]) - height[top];
                ans += distance * bounded_height;
            }
            stack.push(current++);
        }
        return ans;
    }

    // 雙指標
    public int trap03(int[] height) {
        int left = 0, right = height.length - 1;
        int ans = 0;
        int left_max = 0, right_max = 0;
        while (left < right) {
            if (height[left] < height[right]) {
                if (height[left] >= left_max) {
                    left_max = height[left];
                } else {
                    ans += (left_max - height[left]);
                }
                ++left;
            } else {
                if (height[right] >= right_max) {
                    right_max = height[right];
                } else {
                    ans += (right_max - height[right]);
                }
                --right;
            }
        }
        return ans;
    }

總結

上次說到單調遞減棧可以用來統計元素後面比自己大的元素,在這裡也同樣好用

還有雙指標的思想,絕對是將複雜度降低的絕妙方式,我個人就很喜歡這個思想

如果文章存在問題或者有更好的題解,歡迎在評論區斧正和評論,各自努力,最高處見