题目与题解
84.柱状图中最大的矩形
题目链接: 84.柱状图中最大的矩形
代码随想录题解: 84.柱状图中最大的矩形
解题思路:
看答案,不为难自己
看完代码随想录之后的想法
重点还是怎么将应用题翻译成数学题,才能选择合适的做法。
要求最大的矩形,其实就是以某一个最低高度为矩形的高,将其周围不小于它的高度的柱子宽度加在一起,作为矩形的宽。这样翻译过来,就是要求每一个高度左边和右边第一个高度小于它的下标,这样,左右下标之间的柱子就是矩形的宽度。
与接雨水的题稍微有点反过来,略复杂了一些。
1. 双指针法
遍历两遍heights,分别计算其每个下标左边第一个高度小于它的下标minLeftIndex和每个下标右边第一个高度小于它的下标minRightIndex。为了提高计算效率,以计算左边下标为例,不需要逐一遍历其左边的所有数字中直到找到第一个下标,如果其左边某一个下标高度高于它,那直接取t = minLeftIndex[t]为当前最小下标即可,直到t < 0。为了防止死循环,minLeftIndex[0]初始化要赋值为-1。右边同理。
求完这两个数组后,遍历0-size-1,求出每一个下标对应的最大矩阵大小为minRightIndex[i] - minLeftIndex[i]-1)*heights[i],最后取最大值即可。
class Solution {
public int largestRectangleArea(int[] heights) {
// 双指针
int size = heights.length;
int[] minLeftIndex = new int[size];
minLeftIndex[0] = -1;
for (int i = 1; i < size; i++) {
int t = i-1;
while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
int[] minRightIndex = new int[size];
minRightIndex[size-1] = size;
for (int i = size - 2; i >= 0; i--) {
int t = i+1;
while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
minRightIndex[i] = t;
}
int res = heights[0];
for (int i = 0; i < size; i++) {
res = Math.max(res, (minRightIndex[i] - minLeftIndex[i]-1)*heights[i]);
}
return res;
}
}
2. 单调栈
这次的单调栈要变成栈底到栈顶从小到大排列的顺序了,因为要求的是更高的高度对应矩阵的面积。
这样跟接雨水也很类似,如果当前height[i]大于height[s.peek()]直接入栈,如果相等就弹出一个再入栈一个,否则当栈不为空且height[i]小于height[s.peek()]时,循环计算当前矩形的高度mid = height[s.peek()]并弹出栈顶,如果此时栈不为空,就可以获得其左边第一个比它小的高度的下标s.peek()和右边第一个比它小的高度i,得到矩形宽为 i - s.peek() - 1,求得其面积。最后比较得到最大面积即可。
这里需要注意,heights不能直接使用,要先在其前后加两个值0,生成新的矩阵。否则当当前数组是单调的时候,是无法进入计算其面积的循环里面的。
class Solution {
public int largestRectangleArea(int[] heights) {
// 单调栈
Stack<Integer> s = new Stack<>();
int[] newHeight = new int[heights.length+2];
System.arraycopy(heights, 0, newHeight, 1, heights.length);
s.push(0);
int res = 0;
for (int i = 1; i < newHeight.length; i++) {
if (newHeight[i] > newHeight[s.peek()]) {
s.push(i);
} else if (newHeight[i] == newHeight[s.peek()]){
s.pop();
s.push(i);
} else {
while (!s.isEmpty() && newHeight[i] < newHeight[s.peek()]) {
int mid = s.pop();
if (!s.isEmpty()) {
int w = i - s.peek() - 1;
res = Math.max(res, w * newHeight[mid]);
}
}
s.push(i);
}
}
return res;
}
}
遇到的困难
应用题还是得知道怎么翻译。
单调栈的细节也很多,容易写错。
今日收获
最后一题单调栈,还是有点云里雾里,得再练。
收工了!