Largest Rectangle in Histogram

探讨了多种算法解决直方图中寻找最大矩形面积的问题,包括动态规划、中心扩展、优化边界及栈结合O(N)时间复杂度的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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 height = [2,1,5,6,2,3],
return 10.

思路一:动态规划

这里类似于单源点的最短路径问题的 Dijkstra 算法,其实时间复杂度还是蛮高的。

测试时,小数据可以通过,但是对于大数据,出现超时情况。

动态规划算法原理:i 表示间隔的方条个数,j 表示起始的方条位置,left 和 right 表示要求取的方条的范围,这里我们遍历了一下所有的可能情况:

area[i,j] = max {  area[i,k]+area[k+1,j] }   i<=k<=j;

class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int num = height.size();
        if(num<=0)
            return 0;
        if(num==1)
            return height[0];
        vector<vector<int> > area(num, vector<int>(num,0));
        int maxarea = -1;
        for(int i=0;i<num;i++)
            area[i][i] = height[i];
        for(int i=1;i<num;i++)
        {
            for(int j=0;j<num-i;j++)
            {
                int left = j;
                int right = j+i;
                for(int k=left;k<right;k++)
                {
                    int ave1 =  area[left][k]/(k-left+1);                    
                    int ave2 =  area[k+1][right]/(right-k);
                    if(max(ave1,ave2)>maxarea)
                        maxarea = max(ave1,ave2);
                    area[left][right] = min(ave1,ave2)*(right-left+1);
                    if(area[left][right]>maxarea)
                        maxarea = area[left][right];
                }
                    
            }
        }  
        return maxarea;        
    }
};


思路二:中心扩展

对于每一个 height[i] , 寻找以此 height[i] 作为长方形的宽,然后寻找此长方形的最左边界和最右边界。

class Solution {
public:
    int largestRectangleArea(vector<int> &height) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        int num = height.size();
        if(num<=0)
            return 0;
        int maxarea = height[0];
        for(int i=0;i<num;i++)
        {
            int left = i;
            while(left>=0 && height[left]>=height[i])
                left--;
            left++;
            int right = i;
            while(right<num && height[i]<=height[right])
                right++;
            right--;
            int area = height[i]*(right-left+1);
            if(area>maxarea)
                maxarea = area;             
        }
        return maxarea;      
    }
};

这样还只是通过小数据,大数据还是出现超时情况。
 

思路三:优化右边界

对于直方图的每一个有效的右边界,穷举所有的左边界,将面积最大的那个值记录下来。

优化:可以通过选择合适的右边界,做一个剪枝(Pruning)。

观察发现:当 height[k] >= height[k - 1] 时,无论左边界是什么值,选择 height[k] 总会比选择 height[k - 1] 所形成的面积大。

因此,在选择右边界的时候,首先找到一个 height[k] < height[k - 1] 的k,然后取k - 1作为右边界,穷举所有左边界,找最大面积。

如果同时对 左边界 和 右边界 进行剪枝,会漏掉很多情况。

这种算法时间复杂度还是 很高 O(N2),大数据有时未能过。

 
class Solution {  
public:  
    int largestRectangleArea(vector<int> &height) {  
        // Start typing your C/C++ solution below  
        // DO NOT write int main() function  
        int num = height.size();  
        if(num<=0)  
            return 0;  
        int maxarea = height[0];  
        for(int i=0;i<num;i++)  
        {
            //Find the most far right region
            for(int k=i+1;k<num;k++)
            {
                if(height[k-1]<height[k])
                    i = k;
                else
                {
                    i = k-1;
                    break;
                }                
            }      
            int small = height[i];
            for(int j=i;j>=0;j--)
            {
                if(height[j]<small)
                {
                    small = height[j];
                }
                int area = small*(i-j+1);  
                if(area>maxarea)  
                    maxarea = area;  
            }
        }  
        return maxarea;        
    }  
};  


思路四:Stack + O(N) time

用一个栈来来优化对每个 height[i] 左右边界的寻找,从左到右扫描数组:

(1)如果当前栈S为空,则把当前的i值放入栈中;

(2)如果当前节点的高度大于栈顶节点的高度,也把当前的 i 值压入栈中(注意这里的不断压栈其实在寻找栈顶节点的矩形的右边界,即小于栈顶元素的节点);

(3)当当前节点的高度小于于当前栈顶节点的高度时,此节点便是当前栈中所有节点高度值大于该值的长方形的右边界;

(4)剩下的任务就是寻找左边界,栈中每个节点的左边界其实是其栈中的上一个元素。

这样对于数组中每个元素最多进一次栈,出一次栈。所以时间复杂度为O(n)。

可以从每次更新 left 和 right 的地方可以看出来。

 

class Solution {  
public:  
    int largestRectangleArea(vector<int> &height) {  
        // Start typing your C/C++ solution below  
        // DO NOT write int main() function  
        int num = height.size();  
        if(num<=0)  
            return 0;  
        if(num==1)  
            return height[0];  
        int maxarea = height[0];  
        stack<int> mystack;  
        int i=0;  
        while(i<num)  
        {  
            if(mystack.empty() || height[i]>=height[mystack.top()])  
            {  
                mystack.push(i);  
                i++;  
            }  
            else  
            {  
                int tp = mystack.top();  
                mystack.pop();  
                int right = i-1;  
                int left = 0;
                if(!mystack.empty())
                    left = mystack.top()+1;
                int area = height[tp]*(right-left+1);  
                maxarea = (area>maxarea)?area:maxarea;                  
            }  
        }  
        while(!mystack.empty())   // all is accessending  
        { 
            int tp = mystack.top();
            mystack.pop();
            int right = i-1;  
            int left = 0;
            if(!mystack.empty())
                left = mystack.top()+1;
            int area = height[tp]*(right-left+1);  
            maxarea = (area>maxarea)?area:maxarea;              
        }         
        return maxarea;        
    }  
};  


最新 java

public class Solution {
    public int largestRectangleArea(int[] height) {
        Stack<Integer> stack = new Stack<Integer>();
        int maxarea = 0;
        int i=0;
        while(i<height.length){
            if(stack.isEmpty() || height[stack.peek()]<=height[i]){
                stack.push(i);
                i++;
            } else {
                int tp = stack.pop();
                int right = i-1;
                int left = 0;
                if(!stack.isEmpty()){
                    left = stack.peek()+1;
                }
                int temparea = height[tp]*(right-left+1);
                if(temparea>maxarea){
                    maxarea = temparea;
                }
            }
        }
        while(!stack.isEmpty()){
            int tp = stack.pop();
            int right = i-1;
            int left = 0;
            if(!stack.isEmpty()){
                left = stack.peek()+1;
            }
            int temparea = height[tp]*(right-left+1);
            if(temparea>maxarea){
                maxarea = temparea;
            }
        }
        return maxarea;
    }
}



可参考     GeeksforGeeks 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值