原题链接:
题解:
暴力过不了,只能换一种思路——单调栈
本质:
本质是提前过滤不必要的元素: 在单调栈中,通过比较元素大小,能够提前过滤掉一些不必要的元素,从而减少了实际操作的次数。例如,在找下一个更大元素的问题中,当遇到一个元素比栈顶元素小时,就可以直接弹出栈顶元素,因为该栈顶元素在未来的计算中已经不会有机会成为其他元素的下一个更大元素。这样就避免了对这些不必要元素的计算和比较。
例子:
一个例子,方便理解。
考虑数组[3, 4, 2, 7, 5]
。
初始化: 我们使用一个栈来存储数组元素的索引。开始时,栈为空。
遍历数组: 我们从左到右遍历数组。对于数组的每个元素:①当栈不为空且栈顶元素大于当前元素则一直出栈(这样是因为如果右侧b有一个比左侧a小的值,那么我们在遍历b右侧的时候永远都不会考虑到a,因为a既比b大同时还距离当前元素较远,如此这般减少了查询范围,提高了查询效率);②若栈为空,则没有比当下元素更小的值;若栈不为空,则栈顶元素便为当前数左边第一个比它小的数;③将当前元素的下标入栈。
栈的状态: 在遍历数组的过程中,栈内的元素始终保持递增有序。栈内的元素是从栈底到栈顶递增的。
最终结果: 对于每个元素,如果在遍历时找到了左边第一个较小的元素,就记录下来;如果没有找到,就保持默认值(例如 -1)。
下面是步骤的详细说明:
- 对于元素 3,栈为空,打印-1,入栈 3(如果是索引就是0,以下均用值表示)。
- 对于元素 4,比栈顶元素 3 大,打印栈顶元素3,入栈 4。
- 对于元素 2,比栈顶元素 4 小,弹出 4,然后比栈顶元素 3 小,弹出 3,此时栈为空,打印-1,入栈 2。
- 对于元素 7,比栈顶元素 2 大,打印栈顶元素2,入栈 7。
- 对于元素 5,比栈顶元素 7 小,弹出 7,然后比栈顶元素 2 大,打印栈顶元素2,入栈 5。
可以用单调栈解决的问题:
找到下一个(上一个)更大(更小)元素(Next Or Last Element):
给定一个数组,对于每个元素,找到数组中右边第一个比它大的元素。这个问题的解决方案可以通过单调递减栈实现。柱状图中的最大矩形(Largest Rectangle in Histogram):
给定一个柱状图,找到其中的最大矩形面积。该问题可以通过单调递增栈来解决。接雨水(Trapping Rain Water):
给定一个数组表示不同位置的高度,计算这个地形能够接住多少雨水。这个问题也可以通过单调递减栈来解决。
参考模板:
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
stack<int> s;
int a[N];
int main() {
int n; cin >> n;
for (int i = 0;i < n;i++) cin >> a[i];
for (int i = 0;i < n;i++) {
while (!s.empty() && a[i] <= a[s.top()]) s.pop();//第一步:若栈不为空且栈顶元素大于当前元素则一直出栈
if (s.empty()) cout << -1 << " ";//第二步:若栈为空,则没有比当下元素更小的值
else cout << a[s.top()] << " ";//若栈不为空,则栈顶元素便为当前数左边第一个比它小的数
s.push(i);//第三步:将当前元素的下标入栈
}
}