85. Maximal Rectangle

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

一道hard题,但没有规定复杂度要求,所以我先用最基本的方法做了遍。对比在一个一位数组求直方图最大面积问题,每个数对应的可以有的最大面积是以它为最小值的最长序列,所以,找到一个标准开始循环就行。

方法一:暴力循环,O(n^4)

我选的标准就是逐个扫描点,考虑它作为矩形左上角对应的最大矩形面积。当发现1时,外循环停下,开始从这个店向右向下找。假设整个matrix是n*n,这种做法复杂度就是O(n^4), 虽然low了点,但是leetcode还是可以接受的,然而我自己做也就会这么写了。如下:

int maximalRectangle(vector<vector<char>>& matrix) {
    int H = matrix.size();
    if (H == 0) return 0;
    int W = matrix[0].size();

    int i, j, tempi, tempj, tempW;//i,j代表大循环坐标,tempi,tempj代表找矩形的小循环坐标
    int area, maxrect=0;
    for (i = 0; i < H; i++) {
        for (j = 0; j < W; j++) {
            // 找到1之后以它为左上角搜索最大面积
            if (matrix[i][j] == '1') {
                tempW = W;
                for (tempi = i; tempi < H; tempi++) {
                    if (matrix[tempi][j] == '1') {
                        // 换行后如果首字不是1,直接break
                        for (tempj = j; tempj < tempW; ) {
                            if (matrix[tempi][tempj] == '0')
                                break;
                            else
                                tempj++;
                        }
                        // 更新宽度右边界
                        tempW = min(tempW, tempj);
                        // 搜索完每行更新一下最大面积
                        area = (tempi - i + 1) * (tempj - j);
                        maxrect = max(maxrect, area);
                    }
                    else
                        break;
                }
            }
        }
    }
    return maxrect;
}
方法二:动态规划优化,复杂度O(n^3)

同样的思路,但不用每次都向右向下循环。可以先建一个表,存下从这点开始,这一行还能连续有几个1。
然后大循环遇到1停下来只用向下找就可以了。

int maximalRectangle(vector<vector<char>>& matrix) {
    int H = matrix.size();
    if (H == 0) return 0;
    int W = matrix[0].size();

    vector<vector<int>> dp(H, vector<int>(W));
    int i, j, tempi, tempW;
    int maxrect = 0, area;

    // 初始化dp矩阵
    for (i = 0; i < H; i++)
        dp[i][W - 1] = (matrix[i][W - 1] == '1');
    for (i = 0; i < H; i++)
        for (j = W - 2; j >= 0; j--) {
            if (matrix[i][j] == '1')
                dp[i][j] = 1 + dp[i][j + 1];
            else
                dp[i][j] = 0;
        }

    // 枚举所有点,同样作为左上角
    for (i = 0; i < H; i++) {
        for (j = 0; j < W; j++) {
            // 预判,如果此时最大可能面积都不够,直接结束这一行
            if ((H - i)*(W - j) < maxrect) break;
            tempW = dp[i][j];
            for (tempi = i; tempi < H && tempW > 0; tempi++) {
                // 随着最小宽度的变化,可是适时终止判断
                if (tempW > dp[tempi][j]) tempW = dp[tempi][j];
                if (tempW * (H - i) < maxrect) break;

                area = tempW * (tempi - i + 1);
                maxrect = max(area, maxrect);
            }
        }
    }
    return maxrect;
}

这里有两次预判可以注意下,适当的减少了很多不必要的判断。如果当前最大的可能性都不比现有的最大面积大的话,不需要徒劳循环,方法一时间上超过10%,方法二超过了70%,其实基本思路已经get了,但差别就是这么大。

方法三:将二维问题回归一维,O(n^2)

这个厉害了,虽然我也想到了联系之前的直方图找最大面积, largest Rectangle in histogram, 这里可以每一行看做一个直方图,用方法二建表的思想可以先建立一个二维的height矩阵,每一行代表从这一行向上可以有的连续1的个数,然后这一行就是一个直方图问题,然后就可以用stack实现O(n)复杂度的面积,然后循环每一行就可以。简直棒呆!

class Solution {
public:
// 一维数组直方图最大面积的线性方法应该熟悉
int largestRectangleArea(vector<int>& height) {
    if (height.size() == 0) return 0;
    height.push_back(0); // 结尾加0
    int i, maxrect = 0, temp;
    stack<int> s;
    for (i = 0; i < height.size(); ) {
        if (s.empty() || height[i] >= height[s.top()]) {
            s.push(i);
            i++;
        }
        //保持递增序列,遇到右边界弹出计算
        else {
            temp = s.top();
            s.pop();
            if (s.empty())
                maxrect = max(maxrect, height[temp] * i);//没有左边界就直接是现有长度乘它
            else
                maxrect = max(maxrect, height[temp] * (i - s.top() - 1));//有左边界就算长度
        }
    }
    return maxrect;
}

int maximalRectangle(vector<vector<char>>& matrix) {
    int H = matrix.size();
    if (H == 0) return 0;
    int W = matrix[0].size();

    vector<vector<int> > height(H, vector<int>(W, 0));
    int i, j, maxrect = 0;
    // 将每一行都构造成一个直方图
    for (i = 0; i < H; i++) {
        for (j = 0; j < W; j++) {
            if (matrix[i][j] == '0')
                height[i][j] = 0;
            else
                height[i][j] = (i == 0) ? 1 : (height[i - 1][j] + 1);
        }
    }
    // 用之前算法解决每一行的最大面积
    for (i = 0; i < H; i++) {
        maxrect = max(maxrect, largestRectangleArea(height[i]));
    }
    return maxrect;
}

};

总之,这一题在我自己做了O(n^4)的解法后,学习了更优化的两种写法,确实感慨不已。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值