85. Maximal Rectangle

来源:互联网 发布:单片机串口通信原理 编辑:程序博客网 时间:2024/06/16 07:08
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
原创粉丝点击