Solution 1
这个题是之前存水问题的有一个变体,这个我是真的不会了……
不同于前两个存水问题。我们从左和从右只需要分别维护一个“最高柱子”就能完成结果的计算。然而本体的计算目标则是所有可能的边界组合中,结果最大的一个,相当于对每一个组合进行了一次双指针维护,时间复杂度会非常高。
本题学习到了一个新的数据结构:单调栈,通过线性遍历实现对所有可能高度的范围边界的探索。
单调栈就是保证内部所有的元素是单调的,出现较小的就推出栈中所有的大项,这样一次常数时间规模的维护就能够找到所有元素的最近小项的位置。
因此在本题,栈中保存的是对应项的位置,单调约束使用位置对应柱子的高度,这样线性遍历就能直到最近的较矮柱子的位置。从左和从右两次遍历,就能直到当前位置的柱子高度左右两侧最近较矮柱子的位置,其间就能放下一个矩形。最后线性检查一下所有可行矩形的面积,就能够在线性时间复杂度下找到结果。
题解中还有一个进一步的优化:让我们从左遍历完成一次推出操作时,实际上也确定了被推出位置的右边界,因此实际上完成一次遍历就能同时确定左右边界。
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n为输入数据的长度,线性遍历维护单调栈以及线性遍历计算面积
- 空间复杂度: O ( n ) O(n) O(n),其中 n n n为输入数据的长度,单调栈数据占用
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int length = heights.size();
// vector<int> boundLeft(length), boundRight(length);
vector<int> boundLeft(length, -1), boundRight(length, length); // 优化
stack<int> monoLeft; // 单调栈,保存的是最近的小于项位置
for (int i = 0; i < length; ++i) {
while (!monoLeft.empty() && heights[monoLeft.top()] >= heights[i]) {
boundRight[monoLeft.top()] = i; // 优化:直接确定右界
monoLeft.pop();
}
boundLeft[i] = monoLeft.empty() ? -1 : monoLeft.top(); // -1作为左侧缺省项
monoLeft.push(i);
}
// 优化
// stack<int> monoRight; // 单调栈,保存的是最近的小于项位置
// for (int i = length - 1; i >= 0; --i) {
// while (!monoRight.empty() && heights[monoRight.top()] >= heights[i]) {
// monoRight.pop();
// }
// boundRight[i] = monoRight.empty() ? length : monoRight.top(); // length作为右侧缺省项
// monoRight.push(i);
// }
int ans = -1;
for (int i = 0; i < length; ++i) {
// 枚举每一个柱子对应高度的左右界,计算面积
ans = max(ans, (boundRight[i] - boundLeft[i] - 1) * heights[i]);
}
return ans;
}
};
Solution 2
Solution 1的Python实现
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
length = len(heights)
boundLeft, boundRight = [0] * length, [length] * length
monoLeft = list()
for i in range(length):
while monoLeft and heights[monoLeft[-1]] >= heights[i]:
boundRight[monoLeft[-1]] = i
monoLeft.pop()
boundLeft[i] = monoLeft[-1] if monoLeft else -1
monoLeft.append(i)
# monoRight = list()
# for i in range(length - 1, -1, -1):
# while monoRight and heights[monoRight[-1]] >= heights[i]: monoRight.pop()
# boundRight[i] = monoRight[-1] if monoRight else length
# monoRight.append(i)
ans = -1
for i in range(length):
ans = max((boundRight[i] - boundLeft[i] - 1) * heights[i], ans)
return ans