LeetCode 84 Largest Rectangle in Histogram
方法:单调栈
时间复杂度:O(n)
空间复杂度:O(n)
想法:求最大的矩形。这个矩形啊,我们假设说顺着直方图横轴的方向的是长,高度是宽。那么,这个矩形的宽肯定是直方图当中某一个直方的高度。其次,对于任何一个直方(任何一个初始的小矩形),它能构成的最大矩形的长就是它往左往右分别扩展,直到遇到一个严格比它低的位置,所覆盖的长度。知道这一点之后就可以用单调栈来做了,最直白的做法就是左来一遍右来一遍,记录每个下标的left和right,就是左边右边分别第一个严格比它低的位置下标,然后算。单调栈是单调不减的栈,然后一旦遍历到的值比栈顶元素小,栈顶元素边上严格小于它的位置就找到了,就是当前遍历到的位置。
也可以在一次for loop里面写完,因为这个单调栈,当遍历到一个元素,元素比当前栈顶小的时候,如果我们采用上一段讲的单调不减的栈,那么弹出栈顶的时候,栈顶的右边第一个严格小于它的显然就是当前遍历到的值,那么左边第一个严格小于它的是不是弹完之后的栈顶呢?如果填完之后的栈顶严格小于它,那么,是的。如果现在的栈顶跟之前弹出去那个栈顶是一样的值,那么,对于现在已经弹出去的那个值的计算是错误的,但是,因为我们最后会安排一个-1把栈里面的所有元素全部踢完,因此你后面踢出去元素的时候,对于同样高度的、堆在一起的一些小矩形,我们对最左边的那个元素的计算是正确的,这样就保证了最后的res是正确的。如果我们采用的是单调递增的栈呢?那就会导致我们对于同样高度的、堆在一起的一些小矩形,我们对最右边的那个元素的计算是正确的。因此这种写法当中,while (!stack.isEmpty() && h < heights[stack.peek()])或者while (!stack.isEmpty() && h <= heights[stack.peek()])都是对的。
代码:
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i <= n; i++) {
int h = i == n ? -1 : heights[i];
while (!stack.isEmpty() && h < heights[stack.peek()]) {
int index = stack.pop();
right[index] = i;
}
stack.push(i);
}
while (!stack.isEmpty()) stack.pop();
int res = 0;
for (int i = n - 1; i >= -1; i--) {
int h = i == -1 ? -1 : heights[i];
while (!stack.isEmpty() && h < heights[stack.peek()]) {
int index = stack.pop();
left[index] = i;
res = Math.max(res, heights[index] * (right[index] - left[index] - 1));
}
stack.push(i);
}
return res;
}
}
改进:
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
Stack<Integer> stack = new Stack<>();
int res = 0;
for (int i = 0; i <= n; i++) {
int h = i == n ? -1 : heights[i];
while (!stack.isEmpty() && h < heights[stack.peek()]) {
int cur = heights[stack.pop()];
int left = stack.isEmpty() ? 0 : stack.peek() + 1;
int right = i - 1;
res = Math.max(res, cur * (right - left + 1));
}
stack.push(i);
}
return res;
}
}
LeetCode 85 Maximal Rectangle
方法:DP+单调栈
时间复杂度:O(mn),m为行数,n为列数
空间复杂度:O(n)
想法:直接做会比较难想,但是这也是一个经典的题目了,LeetCode把这题放在上面一题后面就是有种隐隐要提示你的感觉。这个题求的是最大全是1的矩形,但其实如果以每行为底,往上数数到不是1为止,组成一个个小矩形(直方),那么会发现就又变成上一题了。简单来讲就是把0当成空白,1当成长宽各位1的小矩形,然后按照每一行当底,每一行每一行地算。所以说,就用一个数组记录往上数的1的个数,这个可以用很简单的dp求得,然后对于每一行为底的直方图,照抄上一题的解法就完了。
代码:
class Solution {
public int maximalRectangle(char[][] matrix) {
if (matrix == null || matrix.length == 0|| matrix[0] == null || matrix[0].length == 0) {
return 0;
}
int m = matrix.length, n = matrix[0].length;
int[] h = new int[n];
int res = 0;
for (int row = 0; row < m; row++) {
for (int j = 0; j < n; j++) {
if (matrix[row][j] == '1') {
h[j] += 1;
}
else {
h[j] = 0;
}
}
Stack<Integer> stack = new Stack<>();
for (int i = 0; i <= n; i++) {
int cur = i == n ? -1 : h[i];
while (!stack.isEmpty() && cur < h[stack.peek()]) {
int height = h[stack.pop()];
int left = stack.isEmpty() ? 0 : stack.peek() + 1;
int right = i - 1;
res = Math.max(res, height * (right - left + 1));
}
stack.push(i);
}
}
return res;
}
}