这道题目比较好理解,在做这道题目之前需要先知道,如何去避免计算不必要的矩形面积。
当我们以数组中的每一个柱子为基准的时候,去计算在左右两侧不包含比自己短的柱子的情况下的最大面积,这样就可以把所有有可能出现的最大面积都遍历一遍,其他的情况是没有必要遍历的。
这句话如何理解:
当我们以第一个柱子为基准,发现左右两侧都没有比此柱子更长的柱子,我们是否需要横向延申此柱子的范围去讨论大小?答案是不需要:
可以看出,如遇到更短的柱子,会在以更短的柱子为基准的情况下去讨论,在以2为基准的时候,若横向延伸,将1包含在内,讨论的情况将会与以1为基准的情况重复,是没有必要的,一旦延申包含更短的柱子,高度就会以更低的柱子为基准,所以我们以每个柱子为基准,这样能够减少重复的情况。
在这个前提下,我们仅仅讨论以每个柱子为基准,在左右两侧包含的柱子高度大于等于当前基准柱子高度所形成的最大面积即可。
在这种情况下,就可以使用单调栈来解决此题,遍历高度数组分别将每个柱子能够向左向右延伸的最远距离记录下来,最后再遍历一遍找出最大值即可。
完整代码如下:
public static int largestRectangleArea(int[] heights) {
// 长度是1直接返回即可
if (heights.length == 1) return heights[0];
// 分别存储左右高度不小于自身的最远位置
int[] right = new int[heights.length];
int[] left = new int[heights.length];
// 单调栈
int[] stack = new int[heights.length];
// 先将索引0存入
int top = 0;
stack[top] = 0;
// 先存储right
for (int i = 1; i < heights.length; i++) {
// 当遍历到的当前i小于栈顶,则出栈至比当前元素小的位置或全部出栈
while(top >= 0 && heights[stack[top]] > heights[i]) {
right[stack[top]] = i-1;
top--;
}
// 当前元素入栈
top++;
stack[top] = i;
}
// 将栈内剩余元素的右索引都变成height数组的长度
for (int i = 0; i <= top; i++) {
right[stack[i]] = heights.length-1;
}
top = 0;
stack[top] = heights.length-1;
// 存储left
for (int i = heights.length-2; i >= 0; i--) {
// 当遍历到的当前i小于栈顶,则出栈至比当前元素小的位置或全部出栈
while(top >= 0 && heights[stack[top]] > heights[i]) {
left[stack[top]] = i+1;
top--;
}
// 当前元素入栈
top++;
stack[top] = i;
}
// 将栈内剩余元素的右索引都变成0
for (int i = 0; i <= top; i++) {
left[stack[i]] = 0;
}
// 遍历数组计算最大值
int max = 0;
for (int i = 0; i < heights.length; i++) {
max = Math.max(heights[i]*(right[i]-left[i]+1), max);
}
return max;
}
代码还有优化的空间,时间复杂度打败94%的代码。