主题
分析

首先,面积最大矩形的高度一定是
枚举每个
- 在
左侧的小于 的最近元素的下标 ,如果不存在则为 。求出了 ,那么 就是矩形最左边那根柱子。如果 ,那么加一后是 ,就是整个 最左边的柱子。 - 在
右侧的小于 的最近元素的下标 ,如果不存在则为 。求出了 ,那么 就是矩形最右边那根柱子。如果 ,那么减一后是 ,就是整个 最右边的柱子。
比如示例 1(上图),选择
枚举
为什么这样做不会漏掉答案?题目本质是计算高×宽的最大值,高是我们枚举的,所以只要保证宽尽量大,答案就一定能被我们枚举计算到。
如何快速计算
答疑
问:为什么一定要找「最近」的?
答:看上面的图,比如
写法一:三次遍历
python
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
left = [-1] * n
st = []
for i, h in enumerate(heights):
while st and heights[st[-1]] >= h:
st.pop()
if st:
left[i] = st[-1]
st.append(i)
right = [n] * n
st.clear()
for i in range(n - 1, -1, -1):
h = heights[i]
while st and heights[st[-1]] >= h:
st.pop()
if st:
right[i] = st[-1]
st.append(i)
ans = 0
for h, l, r in zip(heights, left, right):
ans = max(ans, h * (r - l - 1))
return anscpp
// C++ 版待补充cpp
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
int n = heights.size();
vector<int> left(n, -1);
stack<int> st;
for (int i = 0; i < n; i++) {
int h = heights[i];
while (!st.empty() && heights[st.top()] >= h) {
st.pop();
}
if (!st.empty()) {
left[i] = st.top();
}
st.push(i);
}
vector<int> right(n, n);
st = stack<int>();
for (int i = n - 1; i >= 0; i--) {
int h = heights[i];
while (!st.empty() && heights[st.top()] >= h) {
st.pop();
}
if (!st.empty()) {
right[i] = st.top();
}
st.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, heights[i] * (right[i] - left[i] - 1));
}
return ans;
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。每个元素入栈出栈各至多一次,所以二重循环是 的。 - 空间复杂度:
。
写法二:两次遍历
为了做到两次遍历,以及写法三的一次遍历,首先,把
如果
如果
不会。注意在这种情况下,这两个高为
修改
- 在计算
的过程中,如果栈顶元素 ,那么 就是栈顶元素的 。
python
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
left = [-1] * n
right = [n] * n
st = []
for i, h in enumerate(heights):
while st and heights[st[-1]] >= h:
right[st.pop()] = i
if st:
left[i] = st[-1]
st.append(i)
ans = 0
for h, l, r in zip(heights, left, right):
ans = max(ans, h * (r - l - 1))
return anscpp
// C++ 版待补充cpp
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
int n = heights.size();
vector<int> left(n, -1);
vector<int> right(n, n);
stack<int> st;
for (int i = 0; i < n; i++) {
int h = heights[i];
while (!st.empty() && heights[st.top()] >= h) {
right[st.top()] = i;
st.pop();
}
if (!st.empty()) {
left[i] = st.top();
}
st.push(i);
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, heights[i] * (right[i] - left[i] - 1));
}
return ans;
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。每个元素入栈出栈各至多一次,所以二重循环是 的。 - 空间复杂度:
。
写法三:一次遍历
写法二告诉我们,栈顶出栈时,当前下标就是栈顶的
如果此刻能顺带求出栈顶的
想一想,栈顶的
由于单调栈是底小顶大的,栈顶下面那个柱子的高度一定比栈顶小,所以栈顶下面的值就是
为简化代码逻辑,可以在一开始把
此外,循环结束的时候,栈中还有数据,这些数据也要计算矩形面积。处理这种情况可以再写一个循环,但更简单的办法是,往
python
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
heights.append(-1) # 最后大火收汁,用 -1 把栈清空
st = [-1] # 在栈中只有一个数的时候,栈顶的「下面那个数」是 -1,对应 left[i] = -1 的情况
ans = 0
for right, h in enumerate(heights):
while len(st) > 1 and heights[st[-1]] >= h:
i = st.pop() # 矩形的高(的下标)
left = st[-1] # 栈顶下面那个数就是 left
ans = max(ans, heights[i] * (right - left - 1))
st.append(right)
return anscpp
// C++ 版待补充cpp
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
heights.push_back(-1); // 最后大火收汁,用 -1 把栈清空
stack<int> st;
st.push(-1); // 在栈中只有一个数的时候,栈顶的「下面那个数」是 -1,对应 left[i] = -1 的情况
int ans = 0;
for (int right = 0; right < heights.size(); right++) {
int h = heights[right];
while (st.size() > 1 && heights[st.top()] >= h) {
int i = st.top(); // 矩形的高(的下标)
st.pop();
int left = st.top(); // 栈顶下面那个数就是 left
ans = max(ans, heights[i] * (right - left - 1));
}
st.push(right);
}
return ans;
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。每个元素入栈出栈各至多一次,所以二重循环是 的。 - 空间复杂度:
。其中 为 中的不同元素个数。注意栈中没有重复元素。
专题训练
见下面单调栈题单的「二、矩形」。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府