题目
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;
}
};
思路二:中心扩展
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