前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Leetcode No.85 最大矩形(单调栈)

Leetcode No.85 最大矩形(单调栈)

作者头像
week
发布2022-01-06 10:13:21
2920
发布2022-01-06 10:13:21
举报
文章被收录于专栏:用户画像

一、题目描述

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

示例 1:

代码语言:javascript
复制
输入: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'

二、解题思路

1、单调栈

将输入拆分成一系列的柱状图。为了计算矩形的最大面积,我们只需要计算每个柱状图中的最大面积,并找到全局最大值

于是,本质上是No.84 柱状图中最大的矩形题中优化暴力算法的复用。

代码语言:javascript
复制
import java.util.Deque;
import java.util.LinkedList;

public class Solution3 {
    public int maximalRectangle(char[][] matrix) {
        if(matrix.length<=0)
            return 0;
        int cols = matrix[0].length;
        int[] heights = new int[cols+2];
        // 在左右两侧添加两个哨兵
        // 左侧的哨兵使得不用判空
        heights[0] = 0;
        // 尾部的哨兵能在遍历结束时弹出前面的柱子计算面积
        heights[cols+1] = 0;
        int max=0;
        //可以把每一行当作一组height然后求得每一行得最大矩形,然后不断增加行,不断更新height
        for(int row=0;row<matrix.length;row++){
            //转化为柱状图
            for(int j=0;j<cols;j++){
                if(matrix[row][j]=='1'){
                    heights[j+1]++;
                }else
                    heights[j+1] = 0;
            }
            //求的每一行作为最底层的最大矩形,更新最大值
            max = Math.max(max,largestRectangleArea(heights));
        }
        return max;
    }
    public int largestRectangleArea(int[] heights) {
        int len = heights.length;
        if(len==0)
            return 0;
        if(len==1)
            return heights[0];

        Deque<Integer> deque = new LinkedList<>();
        int max = 0;
        // 添加哨兵,使得不用判断空
        deque.addLast(0);
        for(int i=1;i<len;i++){
            // 形成递增单调栈,弹出比当前元素大的元素
            // 同时开始计算前面不同高度的矩形面积,因为当前元素是前面矩形的宽度的边界
            while(heights[i]<heights[deque.peekLast()]){
                // 以当前柱子高度作为矩形的高度
                int currHeight = heights[deque.pollLast()];
                while(currHeight==heights[deque.peekLast()])
                    deque.pollLast();
                // 得到矩形左边的边界
                int currWidth = i - deque.peekLast() -1;
                max = Math.max(max, currWidth*currHeight);
            }
            deque.addLast(i);
        }
        return max;
    }
}

复杂度分析

时间复杂度:O(mn),其中 m 和 n 分别是矩阵的行数和列数。计算 left 矩阵需要 O(mn) 的时间;对每一行应用柱状图算法需要 O(n) 的时间,一共需要 O(mn) 的时间。

空间复杂度:O(mn),其中 m 和 n 分别是矩阵的行数和列数。我们分配了一个与给定矩阵等大的数组,用于存储每个元素的左边连续 1 的数量。

2、使用柱状图的优化暴力方法

最原始地,我们可以列举每个可能的矩形。我们枚举矩形所有可能的左上角坐标和右下角坐标,并检查该矩形是否符合要求。然而该方法的时间复杂度过高,不能通过所有的测试用例,因此我们必须寻找其他方法。

我们首先计算出矩阵的每个元素的左边连续 1 的数量,使用二维数组 left 记录,其中 lefti 为矩阵第 i 行第 j 列元素的左边连续 1的数量。

随后,对于矩阵中任意一个点,我们枚举以该点为右下角的全 1 矩形。

具体而言,当考察以 matrixi 为右下角的矩形时,我们枚举满足0≤k≤i 的所有可能的 k,此时矩阵的最大宽度就为

lefti,lefti−1,…,leftk

的最小值。

对每个点重复这一过程,就可以得到全局的最大矩形。

我们预计算最大宽度的方法事实上将输入转化成了一系列的柱状图,我们针对每个柱状图计算最大面积。

代码语言:javascript
复制
public class Solution {
    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] 为矩阵第 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;
                }
                int width = left[i][j];
                int area = width;
                for (int k = i - 1; k >= 0; k--) {
                    //当考察以 matrix[i][j] 为右下角的矩形时,我们枚举满足0≤k≤i 的所有可能的 k,此时矩阵的最大宽度就为
                    //left[i][j],left[i−1][j],…,left[k][j]的最小值。
                    width = Math.min(width, left[k][j]);
                    area = Math.max(area, (i - k + 1) * width);
                }
                ret = Math.max(ret, area);
            }
        }
        return ret;
    }
}

复杂度分析

时间复杂度:O(m^2*n),其中 m 和 n 分别是矩阵的行数和列数。计算 left 矩阵需要O(mn) 的时间。随后对于矩阵的每个点,需要 O(m) 的时间枚举高度。故总的时间复杂度为O(mn)+O(mn)⋅O(m)=O(m^2*n)。

空间复杂度:O(mn),其中 m 和 n 分别是矩阵的行数和列数。我们分配了一个与给定矩阵等大的数组,用于存储每个元素的左边连续 1 的数量。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/10/05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、题目描述
  • 二、解题思路
    • 1、单调栈
      • 2、使用柱状图的优化暴力方法
      相关产品与服务
      灰盒安全测试
      腾讯知识图谱(Tencent Knowledge Graph,TKG)是一个集成图数据库、图计算引擎和图可视化分析的一站式平台。支持抽取和融合异构数据,支持千亿级节点关系的存储和计算,支持规则匹配、机器学习、图嵌入等图数据挖掘算法,拥有丰富的图数据渲染和展现的可视化方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档