今天又做了两个单调栈的题目, 思路都不难, 就是有很多小细节需要注意的
先放两道题的地址 : #84 ( hard ) #121 ( easy )
121 买卖股票的最佳时机
题意 : 一个固定顺序的数组找到一前以后两个元素差的最大值
首先, 这个数组定序, 肯定不能直接遍历找最大最小
可以利用一个单调递增栈并设定如下规则 :
- 保存栈底元素bottom
- 大于栈顶的数直接入栈
- 出现小于栈顶的数时, 依次弹出栈中元素并更新最大值 ans = man (ans, top - bottom)
- 栈为空时, 压栈的同时更新bottom值
这样经过一次遍历便可以确定最大值
int maxProfit(vector<int>& prices) {
stack<int> s;
int ans = 0;
int bottom = 0;
if (prices.size() == 0) return 0;
else s.push(prices[0]);
bottom = s.top();
for (int i = 1; i < prices.size(); ++i) {
if (prices[i] > s.top())
s.push(prices[i]);
else if (prices[i] < s.top()) {
while ( !s.empty() && prices[i] < s.top()) {
ans = max(ans, s.top()-bottom);
s.pop();
}
if (s.empty())
bottom = prices[i];
s.push(prices[i]);
}
// cout << "top : " << s.top() << "bottom : " << bottom << endl;
}
ans = max(ans, s.top()-bottom);
return ans;
}
84 柱状图中最大的矩形
题面如下 :
做完上一题后, 看这题标签也是单调栈, 然后就说再做一道, 结果一做又是一个多小时...
自己想了半个小时多没啥结果, 一直没有理清楚它的出入栈的思路, 然后看了下题解, 领悟了一个关键信息 :
找出每一个高度所对应的最大矩形面积
比如, 在上图中, 2对应1, 1对应6, 5对应10, 6对应6, 2对应4, 3对应3 然后取最大值就是10
问题在于, 如何确定 ?
可以知道, 对于每一个高度, 当从它开始向左右两侧扩展时, 等找到两边的高度都小于它时, 那么他所对应的最大面积也就确定了 : 为 \\(height \\times weight\\) 其中, weight的值为右边的下标 - 左边的下标 - 1
比如高度为5的矩形, 从他开始向左右找, 左边低于它的为高度=1, 下标=1的矩形, 右边为高度=2, 下标=4的矩形, 那么\\(weight = ( 4 - 1 - 1) = 2\\) , 其所以高度为5的这个矩形对应的最大面积就是5x2 = 10
如果想到了这一点, 那么最容易想到的方法就是中心扩展法 , 对于每一个矩形都向左右扩展一次, 最后取最大值
但是这样时间复杂度比较高, 如果我们用一个栈只存依次递增的高度, 就可以减少很多操作了
我们定义以下规则 :
- 大于等于栈顶元素的数直接入栈 ( 下标 )
- 遇到小于栈顶元素的数时依次弹出栈顶元素直到栈顶元素小于等于新元素, 弹出的同时求出每一个弹出元素对应的最大矩形面积 ( 右下标为新元素下标, 而左下标取值需要考虑一个问题 : 目前的栈是单调非递减栈, 所以左边的下标不能简单的-1, 而需要弹出到左边元素不等于栈顶元素为止, 这个问题很容易被忽略)
最后ans取最大的矩形面积就可以了.
有一个需要注意的点, 卡了我很久 :
在第一个和最后一个元素入栈的时候, 如何操作?
我最后的解决办法是在数组的开始和末尾分别设置一个高度为0的元素, 这样可以保证对于第一个和最后一个元素操作是统一的, 而不需要特判
还需要注意的一点是在对栈进行pop, top等操作时, 一定要想清楚会不会发生栈为空的情况 如果有这种可能性, 需要加入判定条件, 不然很容易出错.
int countWidth(int l, int i, int r) {
return r - l - 1;
}
int largestRectangleArea(vector<int>& heights) {
stack<int> s;
if (heights.size() == 0) return 0;
int ans = 0, l, r;
heights.push_back(0);
heights.insert(heights.begin(),0);
for (int i = 0; i < heights.size(); ++i) {
if (!s.empty() && heights[i] < heights[s.top()]) {
while (!s.empty() && heights[i] < heights[s.top()]) { // 严格小
int now = s.top();
s.pop();
l = s.top();
r = i;
while(heights[l] == heights[now])
{
s.pop();
l = s.top();
}
ans = max (ans, countWidth(l, now, r) * heights[now]);
}
s.push(i);
}
else if (s.empty() || heights[i] >= heights[s.top()]) {
s.push(i); // 压栈
}
}
return ans;
}