力扣第八十五题-最大矩形

181 阅读3分钟

「这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战

前言

力扣第八十五题 最大矩形 如下所示:

给定一个仅包含 0 和 1 、大小为 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
解释: 最大矩形如上图所示。

一、思路

这一题我刚开始也是看了很久,都没有弄明白要怎么写。后来看了一下力扣的题解,才明白这一题和昨天的 力扣第八十四题-柱状图中最大的矩形 思路是一样的,不过要绕一点而已。

我们知道在一个二维矩阵中,如果要画一个矩形。这个矩形的有边界一定会落在二维矩阵的某一列上。

假设要你画一个矩形:矩形的右边在第三列上,求它的最大面积。如下图所示:

image.png

你会怎么做呢?其实很简单我们可以先找到这一列向左有多个 1,这就是矩形的高。这个题目就变成了在这个柱状图中,找到最大的矩形面积了。题目示意如下所示:

image.png

综上所述,这一题的大致步骤如下所示:

  1. 遍历整个二维矩阵,每个点 left[i][j] 左边连续的 1 (如果自身为 1 长度也需要加一)
  2. 我们取出每一列 left[][j],将每一列都看做 柱状图
  3. 通过 单调栈 找到柱状图中最大的矩形面积
  4. 最终,返回最大的矩形面积即可

这一题我觉得最难的地方就是找到矩阵中的每一个点左边连续 1 的个数,再将得到的结果 left[][] 中的每一列看做一个柱状图。
至于如何使用 单调栈 在柱状图中找到最大的矩形面积,可以看我写的上一篇博客,里面有详细的图解算法。

二、实现

实现代码

实现代码与思路中保持一致

    public int maximalRectangle(char[][] matrix) {
        int row = matrix.length;
        if (row == 0) {
            return 0;
        }
        int col = matrix[0].length;
        int[][] left = new int[row][col];
        // 找到每个点左边有多少个连续1 (包括自己)
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (matrix[i][j] == '1') {
                    // 如果是第一列的1,则结果为1
                    left[i][j] = j == 0 ? 1 : left[i][j - 1] + 1;
                }
            }
        }

        int ret = 0;
        for (int j = 0; j < col; j++) { // 对于每一列,使用基于柱状图的方法
            int[] bottom = new int[row];    // 下边界
            int[] top = new int[row];  // 上边界
            int topIndex = 0;   // 记录上一次更新上边界的位置

            Deque<Integer> stack = new LinkedList<>();
            for (int i = 0; i < row; i++) {
                // 只要栈中的下标对应的高度比当前的高度高,就出栈。保证栈顶为最靠近且值小于的元素
                while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
                    stack.pop();
                }
                bottom[i] = stack.isEmpty() ? -1 : stack.peek();
                stack.push(i);
            }
            stack.clear();
            for (int i = row - 1; i >= 0; i--) {
                while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
                    stack.pop();
                }
                top[i] = stack.isEmpty() ? row : stack.peek();
                stack.push(i);
            }
            // 根据上下边界,求出以 `left[i][j]` 作为高的面积
            for (int i = 0; i < row; i++) {
                ret = Math.max(ret, (top[i] - bottom[i] - 1) * left[i][j]);
            }
        }
        return ret;
    }

测试代码

    public static void main(String[] args) {
        char[][] matrix = {{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}};
        new Number85().maximalRectangle(matrix);
    }

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥

如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~