lintcode 直方图最大矩形覆盖与最大子矩阵

本文介绍如何求解最大直方图面积问题,并利用该算法解决寻找最大全真矩阵的问题,通过动态规划和栈操作实现了高效算法。

最大直方图面积

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.

histogram

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

histogram

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

最简单的思路就是遍历每个值,当遍历到一个值的时候就开始向两边扩张,如上图的例子

数组为{2,1,5,6,2,3}  以1位置为例 :他可以向左扩张到-1位置(不包括-1)

也可以向右扩张到array.size()位置

假设他向右扩张的位置是n 想左扩张的位置是 j,当前位置为i

那么以i高度为宽的矩形最大面就是(j-n-1)*array[i];

同理依次遍历计算其它位置 获取最大面积即可

常规解法时间复杂度为O(n*n) 空间复杂度为O(1)

int largestRectangleArea(vector<int> &height) 
    {
        // write your code here
        if(height.empty())
        return 0;
        int res=0;
        for(int i=0;i<height.size();i++)
        {
            int n=i;
            int j=i;
            while(n >= 0 && height[n] >= height[i])
            {
                n--;
            }
            while(j < height.size() && height[j] >= height[i])
            {
                j++;
            }
            int cur=(j-n-1)*height[i];
            res=res>cur?res:cur;
        }
        return res;
    }
要降低时间复杂度需要使用栈来保存当前位置能向左和右扩张的位置
先开始遍历数组数据,栈为空的时候以及栈顶数据小于当前数组数据

就把当前数组下标加入到栈中,保证了栈是一个单调递增且无重复的栈

如果有一个数据小于栈顶数据,则将栈顶数据逐次弹出知道栈顶数据大于

当前数组数据保证栈的单调递增性质 。。。在逐次弹出的过程中 我们可以

计算出以栈顶位置为中心向左右扩散的最大值,因为栈是逐次递增的使用,栈顶数据

不能向左扩张 所以假设栈顶数据是 j 栈顶后面的数据是n 当前数组数据下标是i

弹出栈顶j后 以j位置为中心的扩张范围就是(j-n-1)如果弹出j后栈为空那么n视为-1

原因就是说 既然array[i]<array[j]所以j自然是不能向右扩张的,而栈又是一个递增栈

所以也不能向栈底扩张。

当数组数据最后一个数据也入栈了 说明栈内的数据都能向右扩张到最远

只需要获取栈能向左扩张的距离

同理此时的范围就是(array.size()-n-1);

在上述分析中 数据只入栈了一次并且也只出栈了一次,所以时间复杂度是在线性范围之内

O(n)代码如下

int largestRectangleArea(vector<int> &height) 
    {
        // write your code here
        if(height.empty())
        return 0;
        int res=0;
        stack<int > index;
        for(int i=0;i<height.size();i++)
        {
            while(!index.empty()&&height[i]<=height[index.top()])
            {
                int j=index.top();
                index.pop();
                int n= index.empty()?-1:index.top();
                int cur=(i-n-1)*height[j];
                res=res>cur?res:cur;
            }
            index.push(i);
        }
        while(!index.empty())
        {
            int j=index.top();
            index.pop();
            int n= index.empty()?-1:index.top();
            int cur=(height.size()-n-1)*height[j];
            res=res>cur?res:cur;
        }
        return res;
    }

问题二

给你一个二维矩阵,权值为FalseTrue,找到一个最大的矩形,使得里面的值全部为True,输出它的面积

样例

给你一个矩阵如下

[
  [1, 1, 0, 0, 1],
  [0, 1, 0, 0, 1],
  [0, 0, 1, 1, 1],
  [0, 0, 1, 1, 1],
  [0, 0, 0, 0, 1]
]

输出6

上述问题中 是一个二维矩阵,所以我们可以按行来处理 采用动态规划的思想

设定一个dp数组 代表着 以第i行为底边 dp[j]该条位置往下连续为真的个数

如果矩阵rangle[i][j]=0  dp[j]不能代表他的底

上图的矩阵可以产生5组dp数组

分别为 

1 1 0 0 1

0 2 0 0 2

0 0 1 1 3

0 0 2 2 4

0 0 0 0 1

那最大矩阵数不就是每个dp数组的最大直方图面积?

所以有了上到题的代码我们可以轻松实现该问题的解

int maximalRectangle(vector<vector<bool> > &matrix) 
    {
        // Write your code here
        if(matrix.empty())
        return 0;
        vector<int > height(matrix[0].size(),0);
        int res=0;
        for(int i=0;i<matrix[0].size();i++)
        if(matrix[0][i]==1)
        {
            height[i]=1;
            res=1;
        }
        for(int i=1;i<matrix.size();i++)
        {
            for(int j=0;j<matrix[0].size();j++)
            {
                if(matrix[i][j]==1)
                height[j]+=1;
                else
                height[j]=0;
            }
            int cur=largestRectangleArea(height);
            res=res>cur?res:cur;
        }
        return res;
        
    }
    int largestRectangleArea(vector<int> height)
{
    // write your code here
    if(height.empty())
    return 0;
    
    int res=0;
    std::stack<int > staRes;
    for(int i=0;i<height.size();i++)
    {
        while(!staRes.empty()&&height[i]<=height[staRes.top()])
        {
            int j=staRes.top();
            staRes.pop();
            int k=staRes.empty()?-1:staRes.top();
            int cur=(i-k-1)*height[j];
            res=res>cur?res:cur;
        }
        staRes.push(i);
    }
    while(!staRes.empty())
    {
    	int j=staRes.top();
    	staRes.pop();
    	int k=staRes.empty()?-1:staRes.top();
    	int cur=(height.size()-k-1)*height[j];
    	res=res>cur?res:cur;
	}
    return res;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值