给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
思路1:暴力求解
求出以第i个元素结尾的最大矩阵的面积,然后取这些数中的最大值
如何计算以第i个元素结尾的最大矩阵的面积:
1.从第i个元素的前一个元素开始,从后往前,寻找 包含第i个元素在内 的最小值【相当于矩阵的宽度从二开始增加】
2.计算出以第i个元素结尾的这个矩阵的宽能够向左延伸的每种情况的最大值即可
//1. O(n^2)
//以第i个元素结尾的最大矩阵的面积
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights==null||heights.length==0){
return 0;
}
int n=heights.length;
int area=heights[0];
int maxArea=heights[0];
int min;
for(int i=1;i<n;i++){
area=heights[i];
//从第i个元素的前一个元素开始,从后往前,寻找 包含第i个元素在内 的最小值
min=heights[i];
for(int j=i-1;j>=0;j--){
// int min=heights[j];
// for(int k=j+1;k<=i;k++){
// min=Math.min(min,heights[k]);
// }
//由于是从后向前计算的,所以每次在向前寻找最小值时,只需要用当前值跟已经计算出来的最小值进行比较即可
min=Math.min(min,heights[j]);
area=Math.max(area,(i-j+1)*min);
}
maxArea=Math.max(maxArea,area);
}
return maxArea;
}
}
思路2:分治思想
由于最大矩阵存在于以下几种情况:
1.确定了最矮柱子之后,矩阵的宽尽可能向两边延伸
2.最矮柱子左边的最大矩阵(子问题)
3.最矮柱子右边的最大矩阵(子问题)
4.最后取上述三值的最大值
//2.O(nlogn)
//由于最大矩阵存在于以下几种情况:
// 1.确定了最矮柱子之后,矩阵的宽尽可能向两边延伸
// 2.最矮柱子左边的最大矩阵(子问题)
// 3.最矮柱子右边的最大矩阵(子问题)
// 4.最后取上述三值的最大值
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights==null||heights.length==0){
return 0;
}
return output(heights,0,heights.length-1);
}
private int output(int[] heights,int start ,int end){
if(start>end){
return 0;
}
//寻找最小值所在下标
int min=start;
for(int i=start+1;i<=end;i++){
if(heights[i]<heights[min]){
min=i;
}
}
return Math.max((end-start+1)*heights[min],Math.max(output(heights,start,min-1),output(heights,min+1,end)));
}
}
思路3:
求解包含第i个元素的最大矩阵面积时,我们只需要分别从当前元素左边和右边找出第一个比当前元素小的元素的下标leftMin[i]和rightMin[i],然后通过此下标就可以计算出包含当前元素的最大矩阵面积 (rightMin[i]-leftMin[i])*heights[i]。
在求解 从当前元素左边找第一个比当前元素小的元素下标时,由于当前元素的前一个元素的leftMin[i-1]已经求出,而如果当前元素小于前一个元素时,我们可以不必再次比较leftMin[i-1]+1到i-1之间的元素【因为leftMin[i-1]是第一个小于hegihts[i-1]的元素,leftMin[i-1]+1到i-1之间的元素一定大于等于heights[i]】,而是直接从leftMin[i-1]处所在元素开始继续比较。
求解 从当前元素右边找第一个比当前元素小的元素下标时同理。
//3.O(n)
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights==null||heights.length==0){
return 0;
}
// 求解包含第i个元素的最大矩阵面积时,我们只需要分别从当前元素左边和右边找出第一个比当前元素小的元素的下标leftMin[i]和rightMin[i],然后通过此下标就可以计算出包含当前元素的最大矩阵面积 (rightMin[i]-leftMin[i])*heights[i]
// 在求解 从当前元素左边找第一个比当前元素小的元素下标时,由于当前元素的前一个元素的leftMin[i-1]已经求出,而如果当前元素小于前一个元素时,我们可以不必再次比较leftMin[i-1]+1到i-1之间的元素【因为leftMin[i-1]是第一个小于hegihts[i-1]的元素,leftMin[i-1]+1到i-1之间的元素一定大于等于heights[i]】,而是直接从leftMin[i-1]处所在元素开始继续比较
// 求解 从当前元素右边找第一个比当前元素小的元素下标时同理
int n=heights.length;
int[] leftMin=new int[n];
leftMin[0]=-1;
for(int i=1;i<n;i++){
//从当前元素的前一个元素开始,往前寻找第一个小于当前元素的值的下标
int left=i-1;
//如果heights[left]大于等于当前元素,就跳到第一次比heights[left]元素小的那个元素处进行比较
while(left>=0&&heights[left]>=heights[i]){
left=leftMin[left];
}
leftMin[i]=left;
}
int[] rightMin=new int[n];
rightMin[n-1]=n;
for(int i=n-2;i>=0;i--){
//从当前元素的下一个元素开始,往后寻找第一个小于当前元素的值的下标
int right=i+1;
//如果heights[right]大于等于当前元素,就跳到第一次比heights[right]元素小的那个元素处进行比较
while(right<n&&heights[right]>=heights[i]){
right=rightMin[right];
}
rightMin[i]=right;
}
int maxArea=0;
for(int i=0;i<n;i++){
maxArea=Math.max(maxArea,(rightMin[i]-leftMin[i]-1)*heights[i]);
}
return maxArea;
}
}