Loading

Hard | LeetCode 85. 最大矩形 | 单调栈

85. 最大矩形

给定一个仅包含 01 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例 1:

img
输入: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的数量。那么对于每一列。就成了下图所示的样子。

fig2

这样这道题就转化成了求柱状图中的最大的矩形。同样参考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;
}
posted @ 2021-07-11 19:08  反身而诚、  阅读(92)  评论(0编辑  收藏  举报