要求
给定一个整型矩阵map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
举例1:
其中,最大的矩形区域有3个1,所以返回3。
举例2:
其中,最大的矩形区域有6个1,所以返回6。
思路
假设矩阵的 – 行数:N,列数:M
时间复杂度为:O(N×2M) -- O(N×M)
主要部分
以每一行作切割,统计当前行作为底的情况下,每个位置往上连续1的数量,用数组height[M]
保存
举例:
-
第1行切割后
{1 0 1 1}
👉 height={1,0,1,1}
-
第2行切割后
{1 1 1 1}
👉 height={2,1,2,2}
-
第3行切割后
{1 1 1 0}
👉 height={3,2,3,0}
👀 怎么确定
height[j]
?核心代码:
height[j]=map[i][j]==0?0:height[j]+1
当map这一行的某个位置元素为0, 那这边对应的
height[j] = 0
, 若不为0,那这边对应的height[j]
为上一次的height[j]+ 1
针对每一次得到的height[M]
都要去看一下目前拿它作为底可以有多大的矩形面积?
👉 于是,将每一次的 height[M]
作为参数传入一个特定的函数取名为maxRecFromButton
,即可拿到该值
👉 注意,该值只是每一行得到的height[M]
算得的最大值,每一行都不一样,因此还需要选出最大的那一个作为结果返回
次要部分 – maxRecFromButton
函数
要做到时间复杂度为O(M), 需要点技巧!
对于height[M]
, 可以将其想象为一张直方图,如 height={3,4,5,4,3,6}
,对应的直方图如下,将每个height[j]
想象成一个柱子,然后求出每一根柱子向左向右扩展出去的最大矩形
核心思想:
考查每一根柱子最大能扩多大,这个行为的实质就是找到柱子左边离它最近且小于它的柱子位置在哪里,以及右边离它最近且小于等于它的柱子位置在哪里【此时不用单调栈结构更待何时?】
微妙的是👉为什么向右要把等于包含进去呢?我认为这里可能一方面为了简化一下栈中的元素类型,使其可以是int型而不用搞成队列型,另一方面向右遍历遇到和前面相等的柱子总会更新一下面积最大值
这里的单调栈与上一题的有一丝丝的区别,关键是将上一题中 -1 的用途以及出栈和入栈条件搞清
-
对于柱子
j
而言,分别向左和向右能扩展到的位置会在哪里呢?用单调栈结构
- 左边的位置是离柱子
j
最近且小于它的高度的那个位置k
再加1 - 右边的位置是离柱子
j
最近且小于它的高度的那个位置i
再减1
- 左边的位置是离柱子
-
找到了两个位置,矩形面积怎么算?
假设我们找到了柱子
j
左边离它最近且小于它的位置k
和其右边离它最近且小于等于它的位置i
, 那对于柱子j
这种高度的柱子,此刻能找到的最大矩形面积为 :recSize = (i - k -1) * height[j]
在全部找完之前,右边找到的位置 i 其实有可能作废,因为搞不好height[i+1] = height[j],所以对于柱子j,i往往可能要等最后才能彻底定下来
用例子的图解直观感受一下:
这里注意遍历过程中的i
值确定和遍历结束后直接让i = M
的操作,因为最后留在栈里的是没有找到右边的扩展位置的,那我们可以人为假设在最右柱子的右侧还有个高度为0的柱子,位置为M
, 而此时求面积的公式中i将替换成M
代码
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
int maxRecFromButton(int height[],int n);
int maxRecsize(int N,