所用代码 java
柱状图中的最大矩形 LeetCode 84
题目链接:柱状图中的最大矩形 LeetCode 84 - 困难
思路
和上面那个接雨水的题类似,我们找的是左右比自己小的值。要注意的是首位都应该加一个0,以避免数据本身是单调的造成没法计算结果。
单调栈解法:
class Solution {
public int largestRectangleArea(int[] heights) {
Deque<Integer> stack = new LinkedList<>();
// 新建一个数组,首位填0,以防止数组是单调递增或递减无法计算高度的情况
int[] newHeights = new int[heights.length + 2];
newHeights[0] = 0;
newHeights[newHeights.length-1] = 0;
for (int i = 0; i < heights.length; i++) {
newHeights[i+1] = heights[i];
}
// 接收结果
int result = 0;
stack.push(newHeights[0]);
for (int i = 1; i < newHeights.length; i++) {
if (newHeights[i] > newHeights[stack.peek()]){
stack.push(i);
}else if (newHeights[i] == newHeights[stack.peek()]){
// 这里可加可不加
stack.pop();
stack.push(i);
}else {
while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]){
// 取出栈顶元素
int mid = stack.pop();
// 高度就是本身
int h = newHeights[mid];
// 找到的是两边都比mid较小的,两边都不能要,所以要多减一个1
int w = i - stack.peek() - 1;
result = Math.max(result, h * w);
}
System.out.println("result = " + result);
stack.push(i);
}
}
return result;
}
}
暴力解法: 超时了!
class Solution {
public int largestRectangleArea(int[] heights) {
int sum = 0;
for (int i = 0; i < heights.length; i++) {
int left = i;
int right = i;
for (;left >= 0;left--){
if (heights[left] < heights[i]) break;
}
for (;right < heights.length; right++){
if (heights[right] < heights[i]) break;
}
int w = right - left - 1;
int h = heights[i];
sum = Math.max(sum, w*h);
}
return sum;
}
}
双指针优化暴力: 空间换时间速度较快
class Solution {
public int largestRectangleArea(int[] heights) {
int result = 0;
// 找到每个位置左右第一个比自己小的下标
int len = heights.length;
int[] left = new int[len];
int[] right = new int[len];
// 找左边第一个比自己小的下标
left[0] = -1; // 边界为-1,初始化以防止进入死循环
for (int i = 1; i < len; i++) {
int t = i-1;
// 当第i个元素小于的第t个元素,就将t=left[t],
// 意思是下标为t的左边第一个小于该值的下标,赋给t再比较,不断循环找出最小值
while (t>=0 && heights[t] >= heights[i]){
t = left[t];
}
left[i] = t;
}
// 找右边第一个比自己小的下标
right[len - 1] = len; // 初始话,右边界最小为自己,只有一个元素才会1-(-1)-1=1
for (int i = len - 2; i>=0; i--){
int t = i + 1;
while (t<len && heights[t] >= heights[i]){
t = right[t];
}
right[i] = t;
}
for (int i = 0; i < len; i++) {
int sum = heights[i] * (right[i] - left[i] - 1);
result = Math.max(sum,result);
}
return result;
}
}
总结
本题和接雨水的题一模一样,接雨水是找两边最大值,求面积是找第一个最小值,方法异曲同工,都是使用单调栈来接收和弹出元素,但是求面积使用的是单调递减的栈,才能找到第一个比自己小的数。