核心思想
单调栈处理”某个元素,距它最近的、比它大/小的元素在哪里”这类问题。
最原始的问题形态
给一个数组,对每个元素,找:
左边第一个比我大的 左边第一个比我小的
右边第一个比我大的 右边第一个比我小的
暴力是 ,单调栈是 。
为什么单调栈能做到?
以”右边第一个比我大的”为例:
nums = [3, 1, 4, 2, 5]
维护一个单调递减栈(存下标):
处理 3:栈 = [3]
处理 1:1 < 3,压栈。栈 = [3, 1]
处理 4:4 > 1,弹出 1 → "1 的右边第一个更大值 = 4" ✓
4 > 3,弹出 3 → "3 的右边第一个更大值 = 4" ✓
压栈。栈 = [4]
处理 2:2 < 4,压栈。栈 = [4, 2]
处理 5:5 > 2,弹出 2 → "2 的右边第一个更大值 = 5" ✓
5 > 4,弹出 4 → "4 的右边第一个更大值 = 5" ✓
压栈。栈 = [5]
关键机制:一个元素被弹出的那一刻,正好是它的”答案”出现的时刻。
例题
通过维护一个递增栈,寻找左边第一个比当前值大的位置,出栈并合并到一个区间中。这样栈到最后就会变成几个不同的无法合并的区间。每个区间取最大值就是最终答案。
struct Item {
int mx;
int mi;
int le;
int len;
};
class Solution {
public:
vector<int> maxValue(vector<int>& nums) {
vector<Item> s;
int n = nums.size();
for (int i = 0; i < n; i ++ ) {
int min_v = nums[i];
int max_v = nums[i];
int l = i;
while (!s.empty() && s.back().mx > min_v) {
auto [top_max, top_min, le, len] = s.back(); s.pop_back();
min_v = min(top_min, min_v);
max_v = max(top_max, max_v);
l = le;
}
s.push_back(Item{max_v, min_v, l, i - l + 1});
}
vector<int> ans(n);
for (auto [top_max, top_min, le, len] : s) {
for (int i = 0; i < len; i ++ ) {
ans[le + i] = top_max;
}
}
return ans;
}
};