LeetCode 85 | 如何从矩阵当中找到数字围成的最大矩形的面积?
本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是LeetCode专题53篇文章,我们一起来看看LeetCode中的85题,Maximal Rectangle(最大面积矩形)。
今天的这道题目和上一篇文章讲的Largest Rectangle in Histogram这题有一定的相似,所以如果没有看过上一篇文章的同学,建议先移步观看一下上一篇。
85题的官方难度是Hard,点赞2757,反对69,通过率37.2%左右。它的情况和84题非常相似,点赞比很高,然后通过率也差不多。虽然它是84题的变形题,但是整体的题目质量还是很高的,没有因为这一点被诟病。那么和84题相比,究竟它的变动在哪里呢,让我们一起来看题目吧。
题意
给定一个只包含0和1的数字矩阵,要求在这个矩阵当中找到一个由1组成的最大面积的矩形,返回这个面积。
我们来看个样例:
Input:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
Output: 6
答案是6,是这一块1围成的矩形:
题解
还是老规矩,我们从最简单的方法入手,一点点推导出最佳的思路。
暴力
首先最简单的当然是暴力,这题让我们寻找一个矩形,直接寻找矩形是有点麻烦的。计算机程序不像人眼,可以直接获取到图形相关的信息,计算机不行,只能获得单个位置的信息。所以我们让程序直接判断矩形是不现实的,但我们可以通过特征点来锁定矩形,这个也是业内常用的套路。
锁定一个矩形的方法一般有两种,第一种是用矩形的中心点和长宽来确定。这一种在各种图像识别和目标检测算法当中经常用到,模型预测的结果就是图像中心点的坐标以及长宽的长度。
第二种方法可以通过矩形的对角线上的两个点来确定,这种方法只适用于和坐标轴平行的矩形。比如下图当中,无论我们知道了(x2, y2), (x3, y3)还是(x1, y1), (x4, y4),我们都可以将这个矩形确定下来。
有了确定矩形的方法之后,我们通过暴力法来求解就简单了。我们通过这些值来枚举所有可能构成的矩形,然后依次遍历矩形中的每一个元素,来判断它们是否全是1,如果是否的话,那么就排除,否则则用来更新答案。
这种方法固然可行,但是估算一下,差不多应该是的规模,显然是我们不能接受的。
分析问题
在暴力解法当中我们遇到了时间复杂度的困难,我们想要优化就必须要解决复杂度的问题,复杂度的问题怎么解决呢?干想肯定是不行的,我们需要转变一下思路,寻找一下突破口。
我们枚举的复杂度规模这么高是因为我们遍历了所有矩形,遍历矩形本身就是一个时间复杂度开销非常大的举动。如果不想遍历矩形,还有什么方法可以得出最大面积呢?如果我们联想一下上一题很容易得出答案。
在上一题84题当中,题目给出的是一个个竖直类型的矩形,要求这些矩形组合当中能够找到的最大面积。
在这题当中我们可以对01的数字矩阵也做这么一个类似的变形,将从底部开始连续延伸的1的数量看成是竖直摆放的矩形的高度,这样我们这题就可以使用上一题的思路进行求解了。
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
比如说上面这个矩阵就可以转变为[4, 0, 0, 3, 0],其实就是我们一列一列看,从最低处往上连续的1的数量。但是这样找到的面积最大值是4,并不是答案的6,原因是因为我们寻找的底层不对,并不一定以最后一行作为底面得到的面积最大。所以我们需要遍历作为底层的行,然后用这种方法寻找最大面积,全局当中找到的最大面积就是答案。
在上一题我们计算矩形面积的时候用到了两个单调栈,分别计算了某一个高度向左、向右能够延伸到的最远距离,其实这并没有必要。因为我们用一个栈也可以同时计算出两边的边界。举个例子:[1, 3, 6, 7],当前元素是5。我们需要把6,7出栈,5入栈。我们知道了5的左边界是3,但仔细想一想,对于7来说,我们知道了它的左右边界。7的左边界是6,右边界是5。也就是说对于栈顶的元素而言,它的左边界是stack[top-1],右边界是当前的位置i,宽就是i - stack[top-1] - 1。
class Solution:
def maximalRectangle(self, matrix: List[List[str]]) -> int:
# 求出行数n和列数m
n = len(matrix)
if n == 0:
return 0
m = len(matrix[0])
# 存储每一层的高度
height = [0 for _ in range(m+1)]
res = 0
# 遍历以哪一层作为底层
for i in range(n):
sk = [-1]
for j in range(m+1):
# 计算j位置的高度,如果遇到0则置为0,否则递增
h = 0 if j == m or matrix[i][j] == '0' else height[j] + 1
height[j] = h
# 单调栈维护长度
while len(sk) > 1 and h < height[sk[-1]]:
res = max(res, (j-sk[-2]-1) * height[sk[-1]])
sk.pop()
sk.append(j)
return res
总结
乍一看这道题好像非常复杂,但是当我们对它进行分析和变形之后,它又变回了普通的单调栈的应用题。在单调栈的使用当中,有两个细节,一个细节是栈在初始化的时候插入了-1,插入-1是作为一个标兵,也就是所有情况能够达到的最左侧的边界。另一个细节是维护结束的时候插入了0,插入0的目的是为了弹出栈内所有的元素,因为只有出栈的元素会计算构成的面积,这样可以保证不会遗漏情况。
除了上面提到的之外,还有其他的一些细节,比如数组的创建的长度,还有矩形面积的计算公式等等。很多时候算法之所以难以实现,也正是因为需要考虑的细节很多,整体的逻辑不是非常清楚,需要我们进行大量的思考。总体来说,这是一道非常优秀的问题,值得大家仔细钻研。
今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、在看、点赞)。
本文使用 mdnice 排版
LeetCode 85 | 如何从矩阵当中找到数字围成的最大矩形的面积?的更多相关文章
- LeetCode (85): Maximal Rectangle [含84题分析]
链接: https://leetcode.com/problems/maximal-rectangle/ [描述] Given a 2D binary matrix filled with '0's ...
- Leetcode 137. 只出现一次的数字 II - 题解
Leetcode 137. 只出现一次的数字 II - 题解 137. Single Number II 在线提交: https://leetcode.com/problems/single-numb ...
- Leetcode 542:01 矩阵 01
Leetcode 542:01 矩阵 01 Matrix### 题目: 给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离. 两个相邻元素间的距离为 1 . Given a matr ...
- [LeetCode] 85. Maximal Rectangle 最大矩形
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and ...
- 【python】Leetcode每日一题-矩阵置零
[python]Leetcode每日一题-矩阵置零 [题目描述] 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 .请使用 原地 算法. 进阶: 一个直观的解 ...
- leetcode[85] Maximal Rectangle
给定一个只含0和1的数组,求含1的最大矩形面积. Given a 2D binary matrix filled with 0's and 1's, find the largest rectangl ...
- 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵
题目描述: 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积) 输入: 每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K 接下 ...
- 禁止苹果浏览器Safari将数字识别成电话号码的方法
偶然发现用ipad访问我的网站时,发现网站上的一串数字变颜色了(原来是红色的),现在变成了蓝色.一开始以为网站出了什么问题,后来在PC端查看,发现颜色依旧是红色.在ipad上点击还会弹出菜单呼叫的选项 ...
- 比较java与c语言中数字转换成字符的不同
java java中将数字转换成字符非常方便,只要用一个"+"然后在跟一个空格行了.比如,你输入一个122 ,就会变成"122 ". import java.u ...
随机推荐
- 一文搞懂InnoDB索引存储结构
参考资料:掘金小册:MySQL 是怎样运行的:从根儿上理解 MySQL B+树 我们知道,InnoDB是用B+树作为组织数据形式的数据结构.不论是存放用户记录的数据页,还是存放目录项记录的数据页,我们 ...
- Spring 面试详解
SpringSpring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象.Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地 ...
- socketserver模块使用与源码分析
socketserver模块使用与源码分析 前言 在前面的学习中我们其实已经可以通过socket模块来建立我们的服务端,并且还介绍了关于TCP协议的粘包问题.但是还有一个非常大的问题就是我们所编写的S ...
- Visual C++线程同步技术剖析:临界区,时间,信号量,互斥量
使线程同步 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作.更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解.正常情况下对这种处理结果的了解应当在其 ...
- SQL Server 索引的含义和特点
索引用于快速找出在某个列中某一特定值的行.不使索引,数据库必须从第一条记录开始读完整个表,直到找到相关行.如果表中查询的列有一个索引,数据库能快速到达一个位置去搜寻数据,而不必查看所有数据. 索引的含 ...
- Apache POI 操作Excel(2)-- POI包引入项目
Apache POI发行版包含对许多文档文件格式的支持.这种支持在几个Jar文件中提供.并非每种格式都需要所有jar.下表显示了POI组件.Maven存储库标记和项目的Jar文件之间的关系. (htt ...
- python之浅谈循环
目录 流程控制之while循环 语法 while+break while+continue while+else while循环的嵌套 流程控制之for循环 语法 for+break for+cont ...
- python之浅谈计算机基础
目录 一.计算机基础之编程 什么是编程语言 什么是编程 为什么要编程 二.计算机组成原理 1. 计算机五大组成 CPU 存储器 输入设备 输出设备 2.计算机五大部分补充 CPU相关 应用程序启动流程 ...
- 痞子衡嵌入式:kFlashFile v1.0 - 一个基于Flash的掉电数据存取方案
大家好,我是痞子衡,是正经搞技术的痞子.今天给大家带来的是痞子衡的个人小项目 - kFlashFile. 痞子衡最近在参与一个基于 i.MXRT1170 的项目,项目有个需求,需要在 Flash 里实 ...
- Spring FactoryBean 缓存
相关文章 Spring 整体架构 编译Spring5.2.0源码 Spring-资源加载 Spring 容器的初始化 Spring-AliasRegistry Spring 获取单例流程(一) Spr ...