动态规划
本题要记录每个柱子左边第一个小于该柱子的下标,以及右边第一个小于该柱子的下标,而不是第一个小于该柱子的高度。然后使用两个下标之间的差作为矩形的底(底不包括两个下标所在位置),当前柱子的高度作为矩形的高,计算当前矩形面积
- 左侧没有比当前柱子低的柱子,说明使用当前柱子为高的矩形的底部可以延申到最左侧,下标记为-1;
- 右侧没有比当前柱子低的柱子,说明使用当前柱子为高的矩形的底部可以延申到最右侧,下标记为 n
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> l_idx(n, -1); // 左侧没有比当前柱子低的,则下标为-1
vector<int> r_idx(n, n); // 右侧没有比当前柱子低的,则下标为 n
// 从左往右遍历
for(int i = 1; i < n; i++){
int j = i - 1;
while(j >= 0 && heights[j] >= heights[i]) j = l_idx[j]; // 利用数组快速回溯
l_idx[i] = j;
}
for(int i = n - 2; i >= 0; i--){
int j = i + 1;
while(j < n && heights[j] >= heights[i]) j = r_idx[j];
r_idx[i] = j;
}
int ans = 0;
for(int i = 0; i < n; i++){
ans = max(ans, heights[i] * (r_idx[i] - l_idx[i] - 1));
}
return ans;
}
};
单调栈
本题是要找每个柱子左右两边第一个小于该柱子的下标,所以从栈底到栈顶的顺序应该是从小到大的顺序,这样栈顶的两个元素和即将入栈的元素就能形成小-大-小,这样就能找到左右两边第一个小于当前柱子的下标
- 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()],计算矩形面积
- 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()],因为要找的是左侧第一个比当前遍历元素小的柱子,且栈内元素用作矩形宽度的做下标,要使用最靠右的下标作为矩形的边界,所以应该先pop,再push(i)
- 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()],直接入栈
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
int ans = 0;
stack<int> st;
st.push(0);
for(int i = 1; i < n; i++){
if(heights[i] > heights[st.top()]){
st.push(i);
}else if(heights[i] == heights[st.top()]){
st.pop(); // 写和不写都行,结果一样,不写会重复计算面积相同的结果,写了不会重复计算
st.push(i);
}else{
while(!st.empty() && heights[i] < heights[st.top()]){
int h_idx = st.top();
st.pop();
int l_idx = -1;
if(!st.empty()){
l_idx = st.top();
}
int r_idx = i;
ans = max(ans, heights[h_idx] * (r_idx - l_idx - 1));
}
st.push(i);
}
}
// 最后需要处理底部延申到最右侧的矩形,右下标r_idx为n
int r_idx = n;
while (!st.empty()) {
int h_idx = st.top();
st.pop();
int l_idx = -1;
if (!st.empty()) {
l_idx = st.top();
}
ans = max(ans, heights[h_idx] * (r_idx - l_idx - 1));
}
return ans;
}
精简后的代码
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
int ans = 0;
stack<int> st;
st.push(0);
// 每次循环计算以i为右下标的矩形的面积
for(int i = 1; i <= n; i++){
while(!st.empty()){
if(i < n && heights[i] >= heights[st.top()]){
// 当前柱子的高度heights[i]不小于栈顶柱子高度,直接入栈,不用计算矩形面积
break;
}
int h_idx = st.top(); // 栈顶元素取出,计算以当前柱子为高的矩形面积
st.pop();
int l_idx = -1; // 栈空时,表示左侧没有柱子,则左侧下标记为l_idx
if(!st.empty()){
// 栈不空时,取出左侧下标l_idx
l_idx = st.top();
}
// 栈顶下标表示矩形高度,栈顶前一个元素表示矩形左侧下标l_idx,当前下标i表示矩形的右侧下标
ans = max(ans, heights[h_idx] * (i - l_idx - 1));
}
if(i < n) st.push(i);
}
return ans;
}
};