LeetCode42.接雨水、LeetCode11. 盛最多水的容器

單調棧、動態規劃、雙指針

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;
    }

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


LeetCode11. 盛最多水的容器

【題目】

給你 n 個非負整數 a1,a2,…,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別爲 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。

說明:你不能傾斜容器,且 n 的值至少爲 2。

在這裏插入圖片描述
【示例】

輸入:[1,8,6,2,5,4,8,3,7]
輸出:49

【思考】

方法一:動態規劃

dp1[x] 從0到x-1,第一個大於等於a[x]的下標,沒有則爲-1
dp2[x] 從n-1到x+1,第一個大於等於a[x]的下標,沒有則爲-1
從兩個方向來討論,可以避免單方向的很多情況的討論

然後遍歷,以當前高度爲高,以左右兩邊最大值到當前的距離爲寬,計算最大值
時間複雜度:O(n2

方法二:雙指針
左右兩個指針放在隊首和隊尾,計算出當前雙指針的面積
然後移動較低指針,重新計算面經,並記錄最大面積值(爲什麼移動較低,思考一下移動較低和較高到下一個柱子,與原有面積的關係就可以得出了)

【代碼】

package LeetCode;

import java.util.Arrays;

public class LeetCode11 {
    /**
     * dp1[x] 從0到x-1,第一個大於等於a[x]的下標,沒有則爲-1
     * dp2[x] 從n-1到x+1,第一個大於等於a[x]的下標,沒有則爲-1
     * 從兩個方向來討論,可以避免單方向的很多情況的討論
     *
     * dp[x] = dp[x-1] {a[x]<=a[x-1]}
     * */
    public int maxArea1(int[] height) {
        if (height==null || height.length==0)
            return 0;

        int n,max,i,j;
        n = height.length;
        int[] dp1 = new int[n];
        int[] dp2 = new int[n];
        Arrays.fill(dp1,-1);
        Arrays.fill(dp2,-1);

        dp1[0] = 0;
        for (i=1; i<n; i++) {
            if (height[i]<=height[i-1] && dp2[i-1]!=-1) {
                dp1[i] = dp1[i-1];
            } else {
                for (j=0; j<i; j++) {
                    if (height[i]<=height[j]) {
                        dp1[i] = j;
                        break;
                    }
                }
            }
        }

        dp2[n-1] = n-1;
        for (i=n-2; i>=0; i--) {
            if (height[i]<=height[i+1] && dp2[i+1]!=-1) {
                dp2[i] = dp2[i+1];
            } else {
                for (j=n-1; j>i; j--) {
                    if (height[i]<=height[j]) {
                        dp2[i] = j;
                        break;
                    }
                }
            }
        }

        int[] res = new int[n];
        max = 0;
        for (i=0; i<n; i++) {
            if (dp1[i]!=-1 && dp2[i]!=-1) {
                res[i] = Math.max((i-dp1[i])*height[i],(dp2[i]-i)*height[i]);
            } else if (dp1[i]!=-1 && dp2[i]==-1) {
                res[i] = (i-dp1[i])*height[i];
            } else if (dp1[i]==-1 && dp2[i]!=-1) {
                res[i] = (dp2[i]-i)*height[i];
            }
            if (max < res[i]) max = res[i];
        }

        return max;
    }

    /**
     * 雙指針法
     * 左右兩個指針放在隊首和隊尾,計算出當前雙指針的面積
     * 然後移動較低指針,重新計算面經,並記錄最大面積值
     * */
    public int maxArea(int[] height) {
        if (height==null || height.length==0)
            return 0;

        int l,r,len,max,min;
        len = height.length;
        l = 0;
        r = len-1;
        max = 0;
        while (l<r) {
            min = Math.min(height[l],height[r]);
            max = Math.max(min*(r-l),max);
            if (height[l]==min) {
                l++;
            } else {
                r--;
            }
        }

        return max;
    }

    public static void main(String[] args) {
        int[] a = {1,8,1};
        System.out.println(new LeetCode11().maxArea(a));
    }
}


【Java 面試那點事】

這裏致力於分享 Java 面試路上的各種知識,無論是技術還是經驗,你需要的這裏都有!

這裏可以讓你【快速瞭解 Java 相關知識】,並且【短時間在面試方面有跨越式提升】

面試路上,你不孤單!
在這裏插入圖片描述

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