面試算法總結——單調棧的應用

前言

筆試面試中,單調棧用的也特別多,屬於數據結構類的,在這裏先總結下,免得以後忘了。這裏只講應用,不太涉及原理。

題目

1 矩形最大面積

首先來個經典例題:
求矩形的最大面積。((leetcode 85 maximal_Recetangle leetcode 84 largest_rectangle_in_histogram))
這個題目就是典型的單調棧。
運用單點棧的時候,首先想到的,應該是,建立一個單調遞增還是單調遞減的單調棧。這裏維護一個單調遞增的單調棧。每次遇到一個小於當前棧頂的數時(假設這個數爲tn),把當前棧中所有比tn大的數,全部彈出棧。彈出棧的過程中,不要忘了,在彈出的過程中,不要忘了做相應的操作(如增加矩形的寬)。
以這組數據爲例:
3 5 10 12 7 8 20
剛開始面積s = 0,首先把3入棧,然後5大於3,5入棧,10大於5,10入棧,12大於10,12入棧,7小於12,12出棧,同時s 更新爲12;10比7大,10出棧,更新s爲102 = 20。然後7入棧,這個時候,7的寬爲3(10,12,7,三個數全部壓成了7)。8入棧,20入棧。所有數遍歷完了之後,棧中內容爲3(1),5(1),7(3),8(1),20(1),s=20。20出棧,s=20,8出棧,s=max(s,82)=20,6出棧,s=max(s,6*5) = 30。。。這樣操作下去。
貼出這個題目AC的代碼:

    
    class R {
        public int x;
        public int y;

        public R(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public int largestRectangleArea(int[] height) {
        if(height.length==0)
            return 0;
        
        Stack<R> stack = new Stack<>();
        //維護一個單調遞zeng的單調棧
        int pos = 0;
        int max = 0;
        stack.add(new R(height[pos++], 1));
        while (pos < height.length) {
           \\ R tr = stack.peek();
            int len = 1;
            while (!stack.isEmpty() && (stack.peek().x > height[pos])) {
                len += stack.peek().y;
                max = Math.max((len - 1) * stack.peek().x, max);
                stack.pop();
            }
            stack.push(new R(height[pos++], len));
        }
        int len = 0;
        while (!stack.isEmpty()) {
            len += stack.peek().y;
            max = Math.max(max, len * stack.pop().x);
        }
     \\   System.out.print(max);
        return max;
    }

這是單調棧的一個典型應用,還有一個變形就是 給你一連串數字,要你找出一連串連續的數字,求出該區間段中所有數的和*該區間中最小值,也是單調棧的典型應用。
單調棧的時間複雜度,幾乎可以認爲爲線性。
單點棧還有一類題目,經常出現,那就是 區間和的最小值

區間和的最小值

下面給一個參考鏈接,已經說的很詳細了。
求數組中區間中最小數*區間所有數和的最大值
一維單調棧就說這麼多吧,下面說說二維單調棧。

2 一維單調棧進階——二維單調棧

先來一個二維單調棧的典型題目。
leetcode最大矩形面積
這裏是二維單調棧的典型應用,遍歷數組,每一層當做一個一維的單調棧去處理。關鍵在於得到單調棧的矩形,這個矩形由上面的矩形傳遞下來。如果當前a[i]爲0,則這個矩形高度爲0,如果a[i]爲1,則矩形面積爲a[i-1]+1。
AC代碼如下:

    class R {
        int x;
        int y;
        public R(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    int dp[][];
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0)
            return 0;
        int len1 = matrix.length;
        int len2 = matrix[0].length;
        dp = new int[len1][len2];
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                dp[i][j] = 0;
            }
        }
        for (int i = 0; i < len2; i++) {
            dp[0][i] = matrix[0][i] == '1' ? 1 : 0;
        }
        Stack<R> stack = new Stack<>();
        int a[] = new int[len2];
        a[0] = matrix[0][0] == '1' ? 1 : 0;
        int max = a[0];
        for (int i = 1; i < len2; i++) {
            if (matrix[0][i] == '1') {
                a[i] = a[i - 1] + 1;
                max = Math.max(max, a[i]);
            } else {
                a[i] = 0;
            }
        }
        for (int i = 1; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                if (matrix[i][j] == '1') {
                    dp[i][j] = dp[i - 1][j] + 1;
                }
                int len = 1;
                while (!stack.isEmpty() && (stack.peek().x > dp[i][j])) {
                    len = len + stack.peek().y;
                    max = Math.max(max, (len - 1) * stack.peek().x);
                    stack.pop();
                }
                stack.push(new R(dp[i][j], len));
            }
            int slen = 0;
            while (!stack.isEmpty()) {
//                System.out.print(stack.peek().x + " " + stack.peek().y + " ");
                slen += stack.peek().y;
                max = Math.max(max, stack.peek().x * slen);
                stack.pop();
            }
//            System.out.println();

        }
        return max;
    }

這時複雜度就是在一維單調棧上面加一維。
這個題目還有一個相關變形,就是二維矩形的最大和。不過這個就完全不是單調棧的揭發了,二十動態規劃,後面有時間再總結。

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