LeetCode Largest Rectangle in Histogram&&Maximal Rectangle

第一题就是让你在一个直方图里面求一个面积最大的矩阵,觉得O(n*n)的方法应该是非常显然了,枚举一个长方形,分别往前往后找到它的界,也就是第一个比这个长方形高度小的位置,然后两个界之差乘以这个长方形的高度就是结果,然后枚举所有长方形求最大值即可。这样的复杂度应该是过不了的,这题枚举长方形确实已经不能再优化了,但是找界的方法,可以用一个叫单调队列的东西在O(1)的时间找到。

我曾经做过这么一道题,一队小朋友排队,要你求每个小朋友往后看,最远能看到哪个位置的小朋友,也就是往后看第一个比自己高的人的位置。这个问题其实可以维护一个单调下降的队列(也可以理解为栈)来实现,具体的做法大概是:首先将第一个人入栈,接下来每次入栈的人如果看到栈顶元素比他矮,就顶掉栈顶元素,然后接着看能不能顶掉栈顶下面这个元素,直到栈顶元素比它大,或者栈空了。

举一个例子,

3 1 4 2 5

每次栈的顺序是

3

3 1

4

2

当4进去的时候,把3,1都顶掉了,这说明3和1 的答案都是4,最后栈里面还有2个元素,说明他们后面没有比他们高的人。这个做法最多每个元素进栈一次,出栈一次,复杂度O(n)。

对于直方图这个题,我只要先花O(N)的时间将每一个长方形左侧和右侧第一个比它小的位置记录下来,然后再花O(n)时间枚举长方形时,只需要O(1)的时间就能获得界,即可得到答案。

代码如下:

int la[100201];
int ra[100201];
int st[100201][2];
class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        int i;

        int n = height.size();
        if (n == 0)
            return 0;
        for (i = 0; i<n; i++)
        {
            la[i] = -1;
            ra[i] = n;
        }
        int top = 0;
        st[top][0] = height[0];
        st[top][1] = 0;
        top++;
        for (i = 1; i<n; i++)
        {
            while (height[i]<st[top - 1][0])
            {
                top--;
                ra[st[top][1]]=i;
                if (top == 0)
                    break;
            }
            st[top][0] = height[i];
            st[top][1] = i;
            top++;
        }

        top = 0;
        st[top][0] = height[n - 1];
        st[top][1] = n - 1;
        top++;
        for (i = n - 2; i >= 0; i--)
        {
            while (height[i]<st[top - 1][0])
            {
                top--;
                la[st[top][1]] = i;
                if (top == 0)
                    break;
            }
            st[top][0] = height[i];
            st[top][1] = i;
            top++;
        }
        int ans = 0;
        for (i = 0; i < n; i++)
        {
            //cout << la[i] << " " << ra[i] << endl;
            if (ans < (ra[i] - la[i] - 1)*height[i])
                ans = (ra[i] - la[i] - 1)*height[i];
        }
        return ans;
    }
};

接下来这题最大全1矩阵,与直方图这题有着非常密切的关系,如果我把这个01矩阵的前面i行加一起(如果中间出现了0就断开了),那么它就是在一个直方图里面找最大的矩阵,所以现在这个问题就是变成了多个直方图的最大矩阵,之所以为多个,是因为,前i行算一个,总共有n个,需要注意的是要处理被0断开的情况。代码非常相似,如下:

class Solution {
public:
    int h[10001];
    int la[100201];
    int ra[100201];
    int st[100201][2];
    int maximalRectangle(vector<vector<char> > &matrix) {
        int i,j;
        int n,m;
        
        n=matrix.size();
        if(n==0)
            return 0;
        m=matrix[0].size();
        memset(h,0,sizeof(h));
        int ans=0;
        for(i=0;i<n;i++)
        {
            for(j=0;j<m;j++)
            {
                if(matrix[i][j]=='1')
                    h[j]++;
                else
                    h[j]=0;
            }
            for (j = 0; j<m ; j++)
            {
                la[j] = -1;
                ra[j] = m;
            }
            int top = 0;
            st[top][0] = h[0];
            st[top][1] = 0;
            top++;
            for (j = 1; j<m; j++)
            {
                while (h[j]<st[top - 1][0])
                {
                    top--;
                    ra[st[top][1]]=j;
                    if (top == 0)
                        break;
                }
                st[top][0] = h[j];
                st[top][1] = j;
                top++;
            }
            top = 0;
            st[top][0] = h[m - 1];
            st[top][1] = m - 1;
            top++;
            for (j = m - 2; j >= 0; j--)
            {
                while (h[j]<st[top - 1][0])
                {
                    top--;
                    la[st[top][1]] = j;
                    if (top == 0)
                        break;
                }
                st[top][0] = h[j];
                st[top][1] = j;
                top++;
            }
            for(j=0;j<m;j++)
                if(ans<h[j]*(ra[j]-la[j]-1))
                    ans=h[j]*(ra[j]-la[j]-1);
        }
        return ans;
    }
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值