没事刷刷题:LeetCode(84) Largest Rectangle in Histogram & (85) Maximal Rectangle

84原题:

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].

 

The largest rectangle is shown in the shaded area, which has area = 10 unit.

 

For example,
Given heights = [2,1,5,6,2,3],
return 10.

 

解决思路:

刚开始打算从动态规划入手,试着写了一下递归方程,发现不合适。但是动态规划提供了一个思路,就是求以当前位置i为最矮柱形时的最大矩形面积,然后按i遍历求最大面积。

如何求以当前位置i为最矮柱形时的最大矩形面积,如果从最朴实的思路考虑,我们对于每个位置i,都分别向左向右遍历,找到对应矩形的左右边界,很容易求取矩形面积,这样的时间复杂度是O(n2)。

这个时间复杂度显然不符合我们的需要。可以考虑优化手段,在向左向右遍历的时候,对于相邻的i,显然有很多重复计算。我们可以设想,假如位置i的柱形,其左边界为Li,右边界为Ri。如果位于i+1的柱形比它更高,那么i+1的左边界是Li+1=ii,右边界Ri+1<=Ri。那么计算i+1的对应矩形,是不影响i的矩形的计算的,我们完全可以计算完i+1的矩阵后,在计算Ri时,把Li+1到Ri+1的遍历过程都省略掉。这种思想好像和栈的思路比较吻合。

设计方案:

设置栈s,遍历柱形,当遍历到的柱形高度大于栈顶高度时,入栈,否则,说明栈顶元素(curr)到达了其右边界(当前遍历到的位置i),弹出栈顶元素,计算其矩形面积,放在结果数组的对应位置。它的左边界等于出栈后栈顶元素pre的index(首先pre一定是矮于curr的,否则在遍历到curr时,由于curr矮于pre,pre会被弹出;其次pre至curr之间的元素一定是高于curr的,否则它们应该保留在栈中,pre不应该位于栈顶)。最终计算结果数组的最大值即可。时间复杂度O(n)。

代码:

#include
#include
#include

using namespace std;
int main(){
    int hs[12] = {0,1,0,2,1,0,1,3,2,1,2,1};
    vector heights;
    for(int i = 0; i < 12; i++){
        heights.push_back(hs[i]);
    }

    stack > stack;
    vector results(6);
    if(heights.size() == 0){
        return 0;
    } else {
        stack.push(make_pair(0, heights[0]));
    }
    for(int i = 1; i < heights.size(); i++){
        //遇到大的元素,弹出
        while(stack.size() != 0 && stack.top().second > heights[i]){
            //计算
            pair curr = stack.top();
            stack.pop();
            int index = curr.first;
            int height = curr.second;
            int pre; //左边比curr小的元素坐标
            if(stack.size() != 0){
                pre = stack.top().first;
            } else {
                pre = -1;
            }
            int post = i;
            results[index] = (post - pre - 1) * height;
        }
        stack.push(make_pair(i, heights[i]));
    }
    while(!stack.empty()){
        pair curr = stack.top();
        stack.pop();
        int index = curr.first;
        int height = curr.second;
        int pre; //左边比curr小的元素坐标
        if(stack.size() != 0){
            pre = stack.top().first;
        } else {
            pre = -1;
        }
        int post = heights.size();
        results[index] = (post - pre - 1) * height;        
    }
    int max = 0;
    for(int i = 0; i < results.size(); i++){
        if(results[i] > max){
            max = results[i];
        }
    }
    cout << max << endl;
    return max;
}

  

 

85原题:

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

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Return 6.

解决思路:

这题如果做过84题就会有巧妙的思路,将N*M的矩阵压成N个大小为M的数组,将数组套到84题的函数中即可。

具体一点,对于第i行,将0-i行的元素“压扁”作为第i个数组,其第j个元素的值等于从i行向前数,连续的1的个数。如题例中,第三行对应的数组为[ 4, 0, 0, 3, 0],然后将数组丢给84行的函数,即求出以第i行为底的矩形的最大面积。最终对N行求最大即可。时间复杂度O(n2)。

代码:

#include
#include
#include

using namespace std;
int getLineResult(vector heights){
    stack > stack;
    vector results(heights.size());
    if(heights.size() == 0){
        return 0;
    } else {
        stack.push(make_pair(0, heights[0]));
    }
    for(int i = 1; i < heights.size(); i++){
        //遇到大的元素,弹出
        while(stack.size() != 0 && stack.top().second > heights[i]){
            //计算
            pair curr = stack.top();
            stack.pop();
            int index = curr.first;
            int height = curr.second;
            int pre; //左边比curr小的元素坐标
            if(stack.size() != 0){
                pre = stack.top().first;
            } else {
                pre = -1;
            }
            int post = i;
            results[index] = (post - pre - 1) * height;
        }
        stack.push(make_pair(i, heights[i]));
    }
    while(!stack.empty()){
        pair curr = stack.top();
        stack.pop();
        int index = curr.first;
        int height = curr.second;
        int pre; //左边比curr小的元素坐标
        if(stack.size() != 0){
            pre = stack.top().first;
        } else {
            pre = -1;
        }
        int post = heights.size();
        results[index] = (post - pre - 1) * height;        
    }
    int max = 0;
    for(int i = 0; i < results.size(); i++){
        if(results[i] > max) max = results[i]; 
    }
    return max;
}

int getMatrixResult(vector > heights){
    vector results;
    
    vector > new_heights;

    int N = heights.size();
    if(N == 0) return 0;

    int M = heights[0].size();
    if(M == 0) return 0;

    for(int i = 0; i < N; i++){
        vector a, b;
        new_heights.push_back(b);
        for(int j = 0; j < M; j++){
            new_heights[i].push_back(0);
        }
    }

    for(int i = 0; i < M; i++){
        int tmp = 0;
        for(int j = 0; j < N; j++){
            if(heights[j][i] == '0'){
                new_heights[j][i] = 0;
            } else {
                if(j == 0){
                    new_heights[j][i] = 1;
                } else {
                    new_heights[j][i] += new_heights[j-1][i] + 1;
                }
            }
        }
    }   

    for(int i = 0; i < N; i++){
        vector line = new_heights[i];
        int r = getLineResult(line);
        results.push_back(r);
    }

    int max = 0;
    for(int i = 0; i < N; i++){
        if(results[i] > max){
            max = results[i];
        }
    }
    return max;
}

int main(){
    int a[4][5] = {{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}};
    vector > d;
    for(int i = 0; i < 4; i++){
        vector tmp;
        for(int j = 0; j < 5; j++){
            tmp.push_back(a[i][j]);
        }
        d.push_back(tmp);
    }
    cout << getMatrixResult(d) << endl;
}

后来看了大神们的源码,思想是类似的,数据结构更为简洁,可以看到分为height、left、right三个数组,表征的是,以当前的位置i,j所在的行作为矩形的底,以matrix[i][j]向上的连续的1的个数作为高的矩形,其高、左边界、右边界的值。(这里的左右边界与我们的解法中的左右边界分别大/小1,即表示的是“1”的取值范围)

行之间的数组存在一定的继承关系,height上的继承自不必说,左右边界具体表现在left[j] = max(left[j], currleft)与right[j] = min(right[j], currright)这两句。以左边界为例,当上一层为“0”时,left[j]继承来的值为0,currleft为从左边遍历来的左边界,max后取值正确;当上一层不为“0”时,对于对于上一层高为height[j]-1的左边界,如果在这一行相应的位置均为1的话,自然最好,left[j]保持不变;不行的话,取从左边取到的左边界currleft,left[j]=max(left[j], currleft)=currleft,是在将左边界尽量向右以满足矩形限制。

    int maximalRectangle(vector>& matrix) {
        if(!matrix.size() || !matrix[0].size()) return 0;
        int m = matrix.size();
        int n = matrix[0].size();
        
        vector left(n, 0);
        vector right(n, n);
        vector height(n, 0);
        
        int maxArea = 0;
        
        for(int i = 0; i < m; i++)
        {
            int currleft = 0, currright = n;
            for(int j = 0; j < n; j++)
            {
                if(matrix[i][j] == '1')
                    height[j]++;
                else
                    height[j] = 0;
            }
            
            for(int j = 0; j < n; j++)
            {
                if(matrix[i][j] == '1')
                    left[j] = max(left[j], currleft);
                else
                {
                    left[j] = 0;
                    currleft = j + 1;
                }
            }
            
            for(int j = n - 1; j >= 0; j--)
            {
                if(matrix[i][j] == '1')
                    right[j] = min(right[j], currright);
                else
                {
                    right[j] = n;
                    currright = j;
                }
                    
            }
            
            for(int j = 0; j < n; j++)
                maxArea = max(maxArea, (right[j] - left[j]) * height[j]);
        }
        
        return maxArea;
    }

  

 

转载于:https://www.cnblogs.com/wilde/p/8378607.html

你可能感兴趣的:(没事刷刷题:LeetCode(84) Largest Rectangle in Histogram & (85) Maximal Rectangle)