题目
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例
输入: [2,1,5,6,2,3]
输出: 10
测试数据
public static void main(String[] args) {
// TODO Auto-generated method stub
// int f[]= {2,1,5,6,2,3}; //10
// int f[]= {2,1,2}; //3
// int f[]= {3,6,5,7,4,8,1,0}; //20
// int f[]= {6,4,2,0,3,2,0,3,1,4,5,3,2,7,5,3,0,1,2,1,3,4,6,8,1,3}; //14
int f[]= {4,2};
System.out.println(largestRectangleArea(f));
}
想复杂了的方法
// 超时。。思路:记忆化数组存储从i到j的最小值,然后纵横遍历,获取最大矩形面积
public static int memo[][];
public static int len;
public static int largestRectangleArea(int[] heights) {
len=heights.length;
memo=new int[len][len];
//初始化
for(int i=0;i<len;i++) {
Arrays.fill(memo[i], -1);
}
// 对角线赋值
for(int i=0;i<len;i++) {
memo[i][i]=heights[i];
}
// 记忆化只改变右上三角区域
int i,j;
for(j=1;j<len;j++) {
for(i=j-1;i>=0;i--) {
memo[i][j]=Math.min(memo[i+1][j], memo[i][j-1]);
}
}
// for(i=0;i<len;i++) {
// for(j=0;j<len;j++) {
// System.out.print(memo[i][j]);
// if(memo[i][j]<0) {
// System.out.print(" ");
// }
// else {
// System.out.print(" ");
// }
// }
// System.out.println();
// }
// 水平
int res=0,from,value;
for(i=0;i<len;i++) {
if(res<memo[i][i]) {
res=memo[i][i];
}
if(i+1<len&&memo[i][i]>memo[i][i+1]) {
from=i;value=memo[i][i+1];
}
else {
from=i;value=memo[i][i];
}
for(j=i+1;j<len;j++) {
if(memo[i][j]!=value) {
if(res<(j-from)*value) {
res=(j-from)*value;
}
if(memo[i][j]<value) {
value=memo[i][j];
}
else {
from=j;
value=memo[i][j];
}
}
}
if(res<(len-from)*value) {
res=(len-from)*value;
}
}
//竖直
for(j=1;j<len;j++) {
if(j-1>=0&&memo[j-1][j]<memo[j][j]) {
from=j;
value=memo[j-1][j];
}
else {
from=j;
value=memo[j][j];
}
for(i=j-1;i>=0;i--) {
if(memo[i][j]!=value) {
if(res<(from-i)*value) {
res=(from-i)*value;
}
if(memo[i][j]<value) {
value=memo[i][j];
}
else {
from=i;
value=memo[i][j];
}
}
}
if(res<from*value) {
res=from*value;
}
}
return res;
}
暴力解决的优化版本
public static int largestRectangleArea(int[] heights) {
int res=0;
int min;
for(int i=0;i<heights.length;i++) {
min=heights[i];
for(int j=i;j<heights.length;j++) {
if(min>heights[j]) {
min=heights[j];
}
if(res<min*(j-i+1)) {
res=min*(j-i+1);
}
}
}
return res;
}
分治思想实现
- 思路:先找到其中最小的(如果有多个最小的,随便哪一个),然后三种情况(最小的乘以最大长度,左边子串最长,右边子串最长)。因为是同类型的,可以通过递归实现。
public static int largestRectangleArea(int[] heights) {
return downSizing(heights, 0, heights.length-1);
}
public static int downSizing(int []f,int from,int to) {
if(from>to)
return 0;
int min=f[from],pos=from;
for(int i=from+1;i<=to;i++) {
if(f[i]<min ) {
min=f[i];
pos=i;
}
}
int min_count=f[pos]*(to-from+1);
int left_count=downSizing(f, from, pos-1);
int right_count=downSizing(f, pos+1, to);
if(min_count>=left_count&&right_count<=min_count) {
return min_count;
}
else if(left_count>=min_count&&left_count>=right_count) {
return left_count;
}
else {
return right_count;
}
}
栈解决(官方的牛皮方法)
- 我的理解:栈中存放原数组下标。且栈顶到栈底中的数组下标对应的值是降序的。一旦不满足该条件,则弹出栈顶元素,直至满足为止。每次弹出栈顶元素都要计算最大矩形面积。
- 理由:降序可以划分边界,之后入栈则又是一个边界(入栈的数据要小于栈顶元素)。两个边界之间的区域即为栈顶元素的面积。
public int largestRectangleArea(int[] heights) {
Stack < Integer > stack = new Stack < > ();
stack.push(-1);
int maxarea = 0;
for (int i = 0; i < heights.length; ++i) {
while (stack.peek() != -1 && heights[stack.peek()] >= heights[i])
maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
stack.push(i);
}
while (stack.peek() != -1)
maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1));
return maxarea;
}