【面試刷題-力扣經典】LeetCode42:接雨水

單調棧、動態規劃

【題目】

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

在這裏插入圖片描述

【輸入】: [0,1,0,2,1,0,1,3,2,1,2,1]

【輸出】: 6


【解法一】:暴力解法

思路:對於數組中的每個元素,我們找出下雨後水能達到的最高位置,等於兩邊最大高度的較小值減去當前高度的值。每次計算的當前元素能存儲的雨水,最後累計即可

  • 首尾元素無須計算
  • 當左右無大於當前元素的值值,當前位置無法存儲雨水
public int method1(int[] arr) {
        if(arr.length==0 || arr==null) return 0;

        int i,j,res,left,right,leftMax,rightMax;

        res = 0;
        for (i=1; i<arr.length-1; i++) {
            left = i;
            right = i;
            leftMax = rightMax = 0;
            // 向左尋找大於當前元素的最大值
            while (left>=0) {
                if (arr[left] > arr[i] && leftMax < arr[left]) leftMax = arr[left];
                left--;
            }
            // 向右尋找大於當前元素的最大值
            while (right<arr.length) {
                if (arr[right] > arr[i] && rightMax < arr[right]) rightMax = arr[right];
                right++;
            }
            if (leftMax != 0 && rightMax != 0) {
                res += Math.min(leftMax,rightMax) - arr[i];
            }
        }

        return res;
    }

【解法二】動態規劃

在暴力方法中,我們僅僅爲了找到最大值每次都要向左和向右掃描一次。但是我們可以用動態規劃的方式提前存儲這個值。

/**
     * 在暴力方法中,我們僅僅爲了找到最大值每次都要向左和向右掃描一次。
     * 但是我們可以提前存儲這個值。因此,可以通過動態編程解決。
     *
     * dp1[x] 從0達到x的時候,已有的最大值
     * dp2[x] 從n-1到達x的時候,已有的最大值
     * */
    public int method2(int[] a) {

        if(a.length==0 || a==null) return 0;

        int i,res;
        int[] dp1,dp2;
        dp1 = new int[a.length];
        dp2 = new int[a.length];

        dp1[0] = a[0];
        dp2[a.length-1] = a[a.length-1];

        for (i=1; i<a.length; i++) {
            dp1[i] = Math.max(dp1[i-1],a[i]);
        }

        for (i=a.length-2; i>=0; i--) {
            dp2[i] = Math.max(dp2[i+1],a[i]);
        }

        res = 0;
        for (i=1; i<a.length-1; i++) {
            if (a[i]<dp1[i] && a[i]<dp2[i]) {
                res += Math.min(dp1[i],dp2[i]) - a[i];
            }
        }

        return res;
    }

【解法三】單調棧

/**
     * 利用單調棧
     * 棧頂元素大於當前元素,當前元素入棧
     * 等於和小於要進行相應的計算
     *
     * 單調棧存下標要比存內容的意義更大,因爲可以通過下標定位到元素
     * */
    public int method3(int[] a) {
        if (a==null || a.length==0) return 0;

        Stack<Integer> stack = new Stack<>();

        int i,res;

        res = 0;
        for (i=0; i<a.length; i++) {
            while (!stack.empty() && a[stack.peek()] < a[i]) {
                int curIndex = stack.pop();
				// 需要把相同的元素全部彈出棧
                while (!stack.empty() && a[curIndex] == a[stack.peek()]) {
                    curIndex = stack.pop();
                }

                if (!stack.empty()) {
                    int leftIndex = stack.peek();
                    int height = Math.min(a[i],a[leftIndex])-a[curIndex];
                    int width = i - leftIndex - 1;
                    res += height * width;
                }
            }
            stack.add(i);
        }

        return res;
    }

注意:單調棧存下標要比存內容的意義更大,因爲可以通過下標定位到元素

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