单调栈:
按题目要求,需要知道每个位置的矩形可以扩展到的最远距离,由于比当前位置的值小的矩形是不可能与当前位置共同组成矩形的,
所以题目可以理解为,寻找当前位置的左右两边第一个比自己小的值,用这两个位置的 index 相减得到宽度,再乘以当前位置的长度,
如此得到当前位置的最大矩形面积,对每个位置都如此,寻找最大的矩形面积。
如此,我们只需要对数组左右个遍历一次,就可以知道每个位置的左右第一个比自己小的值的位置。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if (!len) return 0;
if (len == 1) return heights[0];
vector<int> left(len, -1), right(len, len);
vector<int> s;
for (int i = 0; i < len; ++i) {
while (s.size() && heights[s.back()] >= heights[i]) {
s.pop_back();
}
left[i] = s.empty() ? -1 : s.back();
s.push_back(i);
}
s.clear();
for (int i = len - 1; i >= 0; --i) {
while (s.size() && heights[s.back()] >= heights[i]) {
s.pop_back();
}
right[i] = s.empty() ? len : s.back();
s.push_back(i);
}
int res = 0;
for (int i = 0; i < len; ++i) {
res = max(res, heights[i] * (right[i] - left[i] - 1));
}
return res;
}
};
优化一:
其实只需要一个 for 循环就可以获取到 left 和 right 数组
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if (!len) return 0;
if (len == 1) return heights[0];
vector<int> left(len, -1), right(len, len);
vector<int> s;
for (int i = 0; i < len; ++i) {
while (s.size() && heights[s.back()] > heights[i]) {
right[s.back()] = i;
s.pop_back();
}
left[i] = s.empty() ? -1 : s.back();
s.push_back(i);
}
int res = 0;
for (int i = 0; i < len; ++i) {
res = max(res, (right[i] - left[i] - 1) * heights[i]);
}
return res;
}
};
优化二:
仔细看上面的操作会发现,其实我们既然可以在每次的for循环中获取到左右的值,其实就可以在for循环中直接判断了,这里我们需要用到两个哨兵,数组左右各一个,保证数组的有效性
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int len = heights.size();
if (!len) return 0;
if (len == 1) return heights[0];
vector<int> v (len + 2, 0);
for (int i = 0; i < len; ++i) {
v[i + 1] = heights[i];
}
vector<int> s;
s.push_back(v[0]);
int res = 0;
len += 2;
for (int i = 1; i < len; ++i) {
while (v[s.back()] > v[i]) {
int height = v[s.back()];
s.pop_back();
int width = i - s.back() - 1;
res = max(height * width, res);
}
s.push_back(i);
}
return res;
}
};