1. 程式人生 > 其它 >Hard | LeetCode 85. 最大矩形 | 單調棧

Hard | LeetCode 85. 最大矩形 | 單調棧

85. 最大矩形

給定一個僅包含 01 、大小為 rows x cols 的二維二進位制矩陣,找出只包含 1 的最大矩形,並返回其面積。

示例 1:

輸入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
輸出:6
解釋:最大矩形如上圖所示。

示例 2:

輸入:matrix = []
輸出:0

示例 3:

輸入:matrix = [["0"]]
輸出:0

示例 4:

輸入:matrix = [["1"]]
輸出:1

示例 5:

輸入:matrix = [["0","0"]]
輸出:0

提示:

  • rows == matrix.length
  • cols == matrix[0].length
  • 0 <= row, cols <= 200
  • matrix[i][j]'0''1'

解題思路

此題和 Hard | LeetCode 84. 柱狀圖中最大的矩形 | 單調棧 實際上是一道題, 只不過從一維拓展到了二維上。

這道題首選要計算出每個位置(i, j)處從左往右連續的1的數量。那麼對於每一列。就成了下圖所示的樣子。

這樣這道題就轉化成了求柱狀圖中的最大的矩形。同樣參考LeetCode 84題的列舉邊界和單調棧的兩種思路。這道題很容易寫出答案。

方法一: 柱狀圖最大面積

public int maximalRectangle(char[][] matrix) {
    int m = matrix.length;
    if (m == 0) {
        return 0;
    }
    int n = matrix[0].length;
    // 新建一個二維陣列
    int[][] left = new int[m][n];

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            // 計算matrix[i][j] 左側連續的數字1的數量
            if (matrix[i][j] == '1') {
                left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
            }
        }
    }
    // 使用列舉邊界的方法, 求出柱形圖的最大面積
    int ret = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == '0') {
                continue;
            }
            // 計算以[i, j]為右下角的最大矩形,
            // 也就是說以當前的i作為起始邊界
            int width = left[i][j];
            int area = width;
            // 然後遍歷[0, i]的所有left值作為邊界
            for (int k = i - 1; k >= 0; k--) {
                width = Math.min(width, left[k][j]);
                // 不斷更新面積的最大值
                area = Math.max(area, (i - k + 1) * width);
            }
            ret = Math.max(ret, area);
        }
    }
    return ret;
}

方法二: 單調棧

public int maximalRectangle(char[][] matrix) {
    int m = matrix.length;
    if (m == 0) {
        return 0;
    }
    int n = matrix[0].length;
    int[][] left = new int[m][n];

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] == '1') {
                left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
            }
        }
    }

    int ret = 0;
    // 對於每一列,使用基於柱狀圖的方法
    for (int j = 0; j < n; j++) {

        // 分別找出上面和下面的臨界點
        int[] up = new int[m];
        int[] down = new int[m];
        Arrays.fill(down, m);
        ArrayDeque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < m; i++) {
            while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
                down[stack.pop()] = i;
            }
            up[i] = stack.isEmpty() ? -1 : stack.peek();
            stack.push(i);
        }

        for (int i = 0; i < m; i++) {
            int height = down[i] - up[i] - 1;
            int area = height * left[i][j];
            ret = Math.max(ret, area);
        }
    }
    return ret;
}