系列文章目录
前言
每天进步一点点!
一、背景
比方说有个矩阵组成的字符全是0、1,那么找到这个矩阵里面最大的由1组成的矩阵,返回它的面积。(每个元素框为单位1),如下图的面积是
6.
二、我的思路
1.思路形成流程
我可以对每一个是1的元素开始计算求解,也就是两个for循环。
//二维vector如何访问
for (int i=matrix.size()-1;i>=0;i--)
{
for (int j=matrix[0].size()-1;j>=0;j--)
{
}
}
接下来,具体如何计算这个元素面临的面积计算问题。不妨把当前元素看做是一个符合题目要求的矩阵起点(左上方的那个点),当然终点也昆虫,本文用的是起点。以这个元素为起点,计算它可以扩展的最大的矩阵面积。
接下来,受之前的题目思路影响,我可以记录什么变量呢,用来统计面积信息。先是想到统计当前元素1右侧的元素1的个数,还有它下方的元素个数,根据这两个值,去计算面积。(为什么想到这两个值?其实就是数学里面,给了你一个顶点位置,你自然觉得如果知道了矩阵的长和宽就好了,但是由于长宽不确定,所以只能依据有1的个数当做长宽,实际比这个小)
好了,虚拟的长和宽的值有了,本文的长为col_sum(表示当前元素右侧连续1的个数),宽为row_sum(表示下方连续1的个数),这两个值都包含了当前1数,即数学中实际的长和宽。举个粒子 1 1 1,这样第一个元素的col_sum就为3,第二个元素的col_sum就为2. 可是要不要记录当前元素的下标信息呢?(暂时待考虑,先写)
我该怎么维护col_sum和row_sum呢?用map,可是重复值怎么办?干脆用结构体,等会万一需要下标还可以再加入。
struct inf{
int col_sum;
int row_sum;
inf()
{
col_sum=0;
row_sum=0;
}
inf(int a,int b)
{
col_sum=a;
row_sum=b;
}
};
typedef struct inf node;
接下来来统计col_sum和row_sum,然后用一个二维数组记录它。实际就是使用两个while循环,如果右侧一直为1,就col_sum++,这个过程同时二维数组的列向右移动,最后加上temp_j<matrix[0].size()判断,以防数组溢出。
最后就是将两个值放入二维数组。此时发现无需下标信息,因为二维数组自带下标统计。
for (int i=0;i<matrix.size();i++)
{
int temp_i=i;
for (int j=0;j<matrix[0].size();j++)
{
int temp_j=j;
int col_sum=0;
int row_sum=0;
while(temp_j<matrix[0].size()&&matrix[temp_i][temp_j]=='1')
{
temp_j++;
col_sum++;//右向连续1计数,包含自己在内
}
temp_j=j;//恢复原值
while(temp_i<matrix.size()&&matrix[temp_i][temp_j]=='1')
{
temp_i++;
row_sum++;//下向连续1计数,含自己
}
temp_i=i;//恢复原值
node e(col_sum,row_sum);
my_inf[i].push_back(e);
}
}
插一嘴,我跌了跟头的地方,对二维数组不熟悉,也不会初始化:
vector<vector<node>> my_inf(matrix.size(),vector<node>(matrix[0].size()));
其实第一个参数是行数,但是第二维不能直接使用数字,因为它本质是一维数组,所以只能用数组初始化的办法,用vector(matrix[0].size()),这里可以加上第二个参数,是为初始化的值,比如vector(matrix[0].size(),0);表示所有元素初始化为0.
开始我是这样:
vector<vector<node>> my_inf(matrix.size());
由于只定义了行数,没有定义列数,我采取的办法是:my_inf[i].push_back(e);
不过后来知道了办法就改为了:my_inf[i][j]=e;
好了,有了统计信息,接下来开始操作怎么计算面积?
因为记录的长宽只是可能性的最大值,每一行的col_sum不一样,也就是长不一样,这该怎么计算呢?
我用的办法是,以当前的col_sum值,用一个for循环,把所有能求的长一个个的计算,然后把统计出来的最大的面积取最大,这里举个例子,比如当前的col_sum为7,我就看连续长为7的行有哪些,是的话就累加为面积,不是就停止,依次是6、5、4、3、2、1,代码如下:
int max_sum=0;
for (int i=0;i<my_inf.size();i++)
{
for (int j=0;j<my_inf[0].size();j++)
{
int max_point_sum=0;
if(matrix[i][j]=='1')
{
max_point_sum=max(my_inf[i][j].col_sum,my_inf[i][j].row_sum);
for(int k=my_inf[i][j].col_sum;k>1;k--)//以长度为单位
{
int temp_i=i;
int ans=0;//局部统计某个点的最长度
while(temp_i<i+my_inf[i][j].row_sum&&my_inf[temp_i][j].col_sum>=k)
{
ans+=k;
temp_i++;
}
temp_i=0;
max_point_sum=max(max_point_sum,ans);
}
max_sum=max(max_sum,max_point_sum);
}
}
}
最后的代码:
class Solution {
public:
struct inf{
int col_sum;
int row_sum;
inf()
{
col_sum=0;
row_sum=0;
}
inf(int a,int b)
{
col_sum=a;
row_sum=b;
}
};
typedef struct inf node;
int maximalRectangle(vector<vector<char>>& matrix) {
vector<vector<node>> my_inf(matrix.size());
//二维vector如何访问
for (int i=0;i<matrix.size();i++)
{
int temp_i=i;
for (int j=0;j<matrix[0].size();j++)
{
int temp_j=j;
int col_sum=0;
int row_sum=0;
while(temp_j<matrix[0].size()&&matrix[temp_i][temp_j]=='1')
{
temp_j++;
col_sum++;//右向连续1计数,包含自己在内
}
temp_j=j;//恢复原值
while(temp_i<matrix.size()&&matrix[temp_i][temp_j]=='1')
{
temp_i++;
row_sum++;//下向连续1计数,含自己
}
temp_i=i;//恢复原值
node e(col_sum,row_sum);
my_inf[i].push_back(e);
}
}
int max_sum=0;
for (int i=0;i<my_inf.size();i++)
{
for (int j=0;j<my_inf[0].size();j++)
{
int max_point_sum=0;
if(matrix[i][j]=='1')
{
max_point_sum=max(my_inf[i][j].col_sum,my_inf[i][j].row_sum);
for(int k=my_inf[i][j].col_sum;k>1;k--)//以长度为单位
{
int temp_i=i;
int ans=0;//局部统计某个点的最长度
while(temp_i<i+my_inf[i][j].row_sum&&my_inf[temp_i][j].col_sum>=k)
{
ans+=k;
temp_i++;
}
temp_i=0;
max_point_sum=max(max_point_sum,ans);
}
max_sum=max(max_sum,max_point_sum);
}
}
}
return max_sum;
}
};
可是它超时了???
2.优化第一项
仔细看前面求解的row_sum和col_sum的过程,用了while循环,可是为什么不能利用之前求过的进行累加呢?也就是可以减少while循环。
可我是从第一个结点开始往右边计算的,我计算第二个结点的时候怎么利用前面的(其实也可以,但换个方向更好做)。干脆从字符矩阵的最后一个结点开始做,于是最后一个统计好,倒数第二个就可以利用最后一个的数据进行类加。
比如1 1 0 0,第二个1的统计要比第一个1的统计更先,这个时候统计第一个1的相关数据的时候,看第二个1,判断当前数据前面一个(就是第二个1)是不是1,如果是的话,把他的col_sum+1就是我的,不是1的话,说明我的col_sum=1;row_sum同理。
代码:
for (int i=matrix.size()-1;i>=0;i--)
{
int temp_i=i;
for (int j=matrix[0].size()-1;j>=0;j--)
{
temp_i=i;
int temp_j=j;
int col_sum=0;
int row_sum=0;
int max_point_sum=0;
if(matrix[temp_i][temp_j]=='1')
{
//如果是1,计算col_sum,如果这个前面是1,直接加前面的,如果前面不是,说明自己1
if(temp_j<matrix[0].size()-1&&matrix[temp_i][temp_j+1]=='1'){
col_sum=my_inf[temp_i][temp_j+1].col_sum+1;
}
else{
col_sum=1;
}
if(temp_i<matrix.size()-1&&matrix[temp_i+1][temp_j]=='1'){
row_sum=my_inf[temp_i+1][temp_j].row_sum+1;
}
else{
row_sum=1;
}
node e(col_sum,row_sum);
my_inf[i][j]=e;
}
}
操蛋,还是超时(优化的时候改代码总是会出错的嘛,耗时绝了):
3.优化第二项
这样我就可以推测,超时原因不在上半部分,而是在下半部分。等会,我好想可以边统计边计算面积,因为统计出来的两个量正好可以用在下面作为循环参数控制,这样是不是可以少点时间,说不定就过了呢!!!!
代码:
class Solution {
public:
struct inf{
int col_sum;
int row_sum;
inf()
{
col_sum=0;
row_sum=0;
}
inf(int a,int b)
{
col_sum=a;
row_sum=b;
}
};
typedef struct inf node;
int maximalRectangle(vector<vector<char>>& matrix) {
vector<vector<node>> my_inf(matrix.size(),vector<node>(matrix[0].size()));
int max_sum=0;
//二维vector如何访问
for (int i=matrix.size()-1;i>=0;i--)
{
int temp_i=i;
for (int j=matrix[0].size()-1;j>=0;j--)
{
temp_i=i;
int temp_j=j;
int col_sum=0;
int row_sum=0;
int max_point_sum=0;
if(matrix[temp_i][temp_j]=='1')
{
//如果是1,计算col_sum,如果这个前面是1,直接加前面的,如果前面不是,说明自己1
if(temp_j<matrix[0].size()-1&&matrix[temp_i][temp_j+1]=='1'){
col_sum=my_inf[temp_i][temp_j+1].col_sum+1;
}
else{
col_sum=1;
}
if(temp_i<matrix.size()-1&&matrix[temp_i+1][temp_j]=='1'){
row_sum=my_inf[temp_i+1][temp_j].row_sum+1;
}
else{
row_sum=1;
}
node e(col_sum,row_sum);
my_inf[i][j]=e;
max_point_sum=col_sum;
for(int k=col_sum;k>=1;k--)//以长度为单位
{
int ans=0;//局部统计某个点的最长度
temp_i=i;//很容易忽略
while(temp_i<i+row_sum&&my_inf[temp_i][temp_j].col_sum>=k)
{
ans+=k;
temp_i++;
}
max_point_sum=max(max_point_sum,ans);
}
}
max_sum=max(max_sum,max_point_sum);
}
}
return max_sum;
}
};
结果吧,没用??
可是怎么优化呢?我想不出来了,算了,去看人家的办法吧!
4.优化第三项
喔,求面积的时候,无需用两次循环。我丢,可以啊。
求面积,我为啥用长度加上累加,直接用累加和乘法啊。不以长度为循环,以列数为循环,总的来说,就是:
总共有7层,每一层的长度都不一样对吧,我就先取一层作为当前矩阵,用乘法计算面积,然后我计算两层的时候,肯定要以两层当中长度更短的那个作为长(才是矩阵啊),然后跟一层的判断那个面积大,保留更大的面积即可。就这样一次比一次多一层的加上去,看看面积会更大不。
牛啊啊。
代码:
class Solution {
public:
struct inf{
int col_sum;
int row_sum;
inf()
{
col_sum=0;
row_sum=0;
}
inf(int a,int b)
{
col_sum=a;
row_sum=b;
}
};
typedef struct inf node;
int maximalRectangle(vector<vector<char>>& matrix) {
vector<vector<node>> my_inf(matrix.size(),vector<node>(matrix[0].size()));
int max_sum=0;
//二维vector如何访问
for (int i=matrix.size()-1;i>=0;i--)
{
int temp_i=i;
for (int j=matrix[0].size()-1;j>=0;j--)
{
temp_i=i;
int temp_j=j;
int col_sum=0;
int row_sum=0;
int max_point_sum=0;
if(matrix[temp_i][temp_j]=='1')
{
//如果是1,计算col_sum,如果这个前面是1,直接加前面的,如果前面不是,说明自己1
if(temp_j<matrix[0].size()-1&&matrix[temp_i][temp_j+1]=='1'){
col_sum=my_inf[temp_i][temp_j+1].col_sum+1;
}
else{
col_sum=1;
}
if(temp_i<matrix.size()-1&&matrix[temp_i+1][temp_j]=='1'){
row_sum=my_inf[temp_i+1][temp_j].row_sum+1;
}
else{
row_sum=1;
}
node e(col_sum,row_sum);
my_inf[i][j]=e;
max_point_sum=col_sum;
int length=col_sum;
//之所以加一是因为宽度为1的不用计算,直接计算两层起的矩阵
for(int k=temp_i+1;k<i+row_sum;k++)//以长度为单位改为以列数为单位
{
length=min(length,my_inf[k][temp_j].col_sum);//以每一行最短的为宽
int ans=length*(k-i+1);//面积计算公式
max_point_sum=max(max_point_sum,ans);
}
}
max_sum=max(max_sum,max_point_sum);
}
}
return max_sum;
}
};
好的,通过,但是时间还是过高,68ms,分析复杂度发现为o(m*n)·o(m),前面那部分是统计两个值,后面那个是用来求面积的。
总算通过了,可是这题的标签是用栈,我没用,说明还可以优化,只是思维不对。
怎么优化呢?
三、官方的思路
好吧,需要做另一个题目,可是我还没做那个题目,之后在回来看看,为了文章完整性,还是把思路和方法贴在这里。
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size();
if (m == 0) {
return 0;
}
int n = matrix[0].size();
vector<vector<int>> left(m, vector<int>(n, 0));
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++) { // 对于每一列,使用基于柱状图的方法
vector<int> up(m, 0), down(m, 0);
stack<int> stk;
for (int i = 0; i < m; i++) {
while (!stk.empty() && left[stk.top()][j] >= left[i][j]) {
stk.pop();
}
up[i] = stk.empty() ? -1 : stk.top();
stk.push(i);
}
stk = stack<int>();
for (int i = m - 1; i >= 0; i--) {
while (!stk.empty() && left[stk.top()][j] >= left[i][j]) {
stk.pop();
}
down[i] = stk.empty() ? m : stk.top();
stk.push(i);
}
for (int i = 0; i < m; i++) {
int height = down[i] - up[i] - 1;
int area = height * left[i][j];
ret = max(ret, area);
}
}
return ret;
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/maximal-rectangle/solution/zui-da-ju-xing-by-leetcode-solution-bjlu/
来源:力扣(LeetCode)
总结
从没有思路到通过代码,耗时一天。其实我就是这样习得技能的,哪怕没有思路,一点点的想,就会在原有思路上碰到问题解决问题,从开始不懂到学会去优化,这也是一个进步呀。
虽然后面还是看了官方的题解,不过代码是我自己敲出来的呀,也还行啦!!