【LeetCode】85. 最大矩形 (單調棧經典應用)

給定一個僅包含 0 和 1 的二維二進制矩陣,找出只包含 1 的最大矩形,並返回其面積。
示例:
輸入:
[
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
輸出: 6
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/maximal-rectangle
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。


思路

根據這個圖,我們很容易可以算出,每個1的上面有幾個連續的1,例如樣例:

  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]

我們可以求出一個h[][],用於表示,每個1的作爲底,他的高度爲多少,此處只需要遍歷一遍數組即可求出,此處複雜度爲O(NM)
那麼這個例子求出來的h[][]就是

-> 1 0 1 0 0 
-> 2 0 2 1 1 
-> 3 1 3 2 2 
-> 4 0 0 3 0 

我們可以把每個數字看作是一個高度爲h[i][j]的柱子。
遍歷每一行,求出以該行爲底,最大矩形面積爲多少。
最後在所有行的答案中取一個最大值,即爲最終答案。

那麼我們只要求出一行中的最大面積即可。


以第三行舉例:

3 1 3 2 2

假設我們求出兩個數組l[]r[],用於表示第i個柱子最左邊可以延長到的位置與最右邊可以延長的位置,
也就是
l[i]保存比第i個柱子小的,在i左邊的最接近i的柱子的下標。(若左邊沒有比i小的,則l[i] = -1
r[i]保存比第i個柱子小的,在i右邊的最接近i的柱子的下標。(若右邊沒有比i小的,則r[i] = len
那麼第三行對應的l[i]r[i]

下標爲 0 到 len - 1 
h[] =  3  1 3 2 2

l[] = -1 -1 1 1 1
r[] =  1  5 3 5 5

當我們求出l[]r[]的時候,我們可以算出對應i當前的高度的矩形面積爲(r[i] - l[i] - 1) * h[i]


那麼問題就剩下一個:如何求出l[]r[]

  • 不是很簡單嘛,對於每個i,往左掃描求出l[i],再往右掃描求出r[i]
    這樣的確很簡單,但是對於每個數字,都需要左右掃描一遍,求出一行中的所有l[i]r[i],則是O(m * m)。再加上我們是一個矩形,有n行,那麼複雜度就是O(n * m * m)
  • 單調棧
    單調棧是一個經典算法。在一個棧內維護一個單調的序列,可以爲單調遞增,也可以單調遞減。
    在這個題目的背景中,我們的目標是,對於每一個i,找到他左邊第一個比他小的。相反方向同理。

我們以一個方向舉一個例子:如何求出l[]

對於每個i,從右往左看,我們只關心第一個小於當前高度的數字在什麼位置,而不在乎更左邊的數字是多少,也就是說,當有一個小的數字出現以後,再往左的大數字是沒有意義的。
例如

h[] = 3 2 1 5 6

我們站在數字5的位置往左看。找到1以後就可以不必往左找了,再往左的2,3都是沒有意義的。因爲數字1限定了當前的上限。
那麼我們在維護棧的時候,如果要入棧一個較小的數字,那麼大於這個數字的所有數都可以出棧了。因爲小的數字已經限定了高度的上限。
所以自然而然我們從左到右就維護出了一個遞增序列。(還不明白就手算一下上面的h[]是如何計算l[]的)

用單調棧的原理,我們可以一遍掃描就求出l[],複雜度O(m)。再反方向掃一遍求出r[]
對於n行,每一行都這樣求一次,總複雜度就爲O(n * m)


代碼

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        int n = matrix.size();
        if(n == 0) return 0;
        int m = matrix[0].size();
        if(m == 0) return 0;
        int dp[n + 10][m + 10] = {0};
        int h[n + 10][m + 10] = {0};
        for (int j = 0; j < m; j++) {
            for (int i = 0; i < n; i++) {
                if(i == 0){
                    h[i][j] = (matrix[i][j] == '1');
                } else {
                    h[i][j] = matrix[i][j] == '1' ? h[i - 1][j] + 1 : 0; 
                }
            }
        }
        int ans = 0;
        int l[m + 10] = {0};
        int r[m + 10] = {0};

        vector<int> st;
        for (int i = 0; i < n; i++) {
            st.clear();
            for (int j = 0; j < m; j++) {
                while(!st.empty() && h[i][st.back()] >= h[i][j]) {
                    st.pop_back();
                }
                if (st.empty()){
                    l[j] = -1;
                } else {
                    l[j] = st.back();
                }
                st.push_back(j); 
            }
            st.clear();
            for (int j = m - 1; j >= 0; j--) {
                while (!st.empty() && h[i][st.back()] >= h[i][j]) {
                    st.pop_back();
                }
                if (st.empty()) {
                    r[j] = m;
                } else {
                    r[j] = st.back();
                }
                st.push_back(j);
                ans = max(ans, (r[j] - l[j] - 1) * h[i][j]);
            }
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章