题目:
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
解析:
在查看所有可能的最大矩形后:发现最大矩形中
其结果很明显是:短的矩形条然后向左右延伸不包括其左边第一个小于该柱子的矩形条,不包括其右边第一个大于该柱子的矩形条,所形成的的矩形。
代码如下:超时
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int result = 0;
for(int i = 0; i < heights.size(); i++){
int left, right;
left = right = i;
for(int l = i-1; l >= 0; --l){
if(heights[l] < heights[i]) break;
left = l;
}
for(int r = i+1; r < heights.size(); ++r){
if(heights[r] < heights[i]) break;
right = r;
}
if(result < (right-left+1)*heights[i]) result = (right-left+1)*heights[i];
}
return result;
}
};
动态规划解法:
这个可以过,原因在于:求当前值左边更大值的下标可以通过dp数组对应的前面一个值来取巧,而并不需要从当前值向左遍历所有情况来取得当前值左边更大值的下标。取得当前值右边更大值的下标同理。
代码:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
vector<int> minLeftIndex(heights.size());
vector<int> minRightIndex(heights.size());
int size = heights.size();
// 记录每个柱子 左边第一个小于该柱子的下标
minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
for (int i = 1; i < size; i++) {
int t = i - 1;
// 这里不是用if,而是不断向左寻找的过程
while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
// 记录每个柱子 右边第一个小于该柱子的下标
minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
for (int i = size - 2; i >= 0; i--) {
int t = i + 1;
// 这里不是用if,而是不断向右寻找的过程
while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
minRightIndex[i] = t;
}
// 求和
int result = 0;
for (int i = 0; i < size; i++) {
int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
result = max(sum, result);
}
return result;
}
};
单调栈:
本题是要找每个柱子左右两边第一个小于该柱子的柱子,因此当遍历到某值小于栈顶时,栈顶对应的柱体的最大面积就找到了。因此单调栈里的顺序是从小到大。
栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度。
剩下就是分析清楚如下三种情况:
代码如下:
需要注意的是当当前元素等于栈顶元素的时候,栈顶元素可以删除可以不删除,因为这两个柱体的最大面积是一样的,只保留一个也可,两个都计算也可。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st;
heights.insert(heights.begin(), 0); // 数组头部加入元素0
heights.push_back(0); // 数组尾部加入元素0
st.push(0);
int result = 0;
// 第一个元素已经入栈,从下标1开始
for (int i = 1; i < heights.size(); i++) {
// 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下标
if (heights[i] > heights[st.top()]) {
st.push(i);
} else if (heights[i] == heights[st.top()]) {
st.pop(); // 这个可以加,可以不加,效果一样,思路不同
st.push(i);
} else {
while (heights[i] < heights[st.top()]) { // 注意是while
int mid = st.top();
st.pop();
int left = st.top();
int right = i;
int w = right - left - 1;
int h = heights[mid];
result = max(result, w * h);
}
st.push(i);
}
}
return result;
}
};
因为这三种情况都有push操作,所以可以化简:如下
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
stack<int> st;
heights.insert(heights.begin(), 0); // 数组头部加入元素0
heights.push_back(0); // 数组尾部加入元素0
st.push(0);
int result = 0;
for (int i = 1; i < heights.size(); i++) {
while (heights[i] < heights[st.top()]) {
int mid = st.top();
st.pop();
int w = i - st.top() - 1;
int h = heights[mid];
result = max(result, w * h);
}
st.push(i);
}
return result;
}
};