19 力扣热题刷题记录之第85题最大矩形

系列文章目录

力扣刷题记录


前言

每天进步一点点!


一、背景

比方说有个矩阵组成的字符全是0、1,那么找到这个矩阵里面最大的由1组成的矩阵,返回它的面积。(每个元素框为单位1),如下图的面积是
6.
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

二、我的思路

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)

总结

从没有思路到通过代码,耗时一天。其实我就是这样习得技能的,哪怕没有思路,一点点的想,就会在原有思路上碰到问题解决问题,从开始不懂到学会去优化,这也是一个进步呀。

虽然后面还是看了官方的题解,不过代码是我自己敲出来的呀,也还行啦!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值