单调栈是用std::stack或vector手动维护的递增/递减栈,核心用于求左右侧第一个更大/更小元素;常用递减栈找右侧更大值,栈中存下标,需注意空栈检查、遍历方向与边界处理。

单调栈的C++核心实现逻辑
单调栈不是标准库容器,而是用 std::stack 或 std::vector 手动维护的栈结构,关键在于“栈内元素严格/非严格递增或递减”。它不解决所有序列问题,只在需要“左侧/右侧第一个更大/更小元素”这类场景才真正高效——比如接雨水、每日温度、柱状图最大矩形。
最常用的是递增栈(栈底到栈顶从小到大),用于找每个元素右侧第一个更小值;递减栈则相反。别硬记“增栈找小”,看实际需求:你要找“下一个更大元素”,就用递减栈,让大的留在栈底。
- 用
std::stack<int></int>存下标比存值更通用,方便反查原数组 - 不要用
std::stack的top()直接修改栈顶——它只读,想更新得弹出再压入 - 初始化栈时一般为空,但处理边界(如无右侧更小元素)时需约定返回 -1 或
INT_MAX
LeetCode 496 下一个更大元素 I 的典型写法
这题是单调栈入门必练,本质是“对 nums1 中每个数,在 nums2 中找它右边第一个更大值”。难点不在栈本身,而在哈希映射和遍历顺序。
必须先遍历 nums2 构建映射表,再查 nums1——不能反过来,否则无法保证“右边”的相对位置。栈里压的是下标,但比较用的是 nums2[i] 的值。
立即学习“C++免费学习笔记(深入)”;
- 循环中每遇到一个新元素
nums2[i],只要它大于栈顶对应值,就持续弹栈并记录结果 - 弹栈条件是:
!st.empty() && nums2[i] > nums2[st.top()],注意括号和运算符优先级 - 最后用
std::unordered_map存{nums2[idx], next_greater},避免重复查找
std::stack<int> st;
std::unordered_map<int, int> mp;
for (int i = 0; i < nums2.size(); i++) {
while (!st.empty() && nums2[i] > nums2[st.top()]) {
mp[nums2[st.top()]] = nums2[i];
st.pop();
}
st.push(i);
}
容易崩的三个细节
写错一个符号,整个逻辑就失效,而且往往测试用例能过但边界挂掉。
- 空栈检查漏了
!st.empty(),直接调st.top()→std::out_of_range或未定义行为 - 用
vector模拟栈时,误把back()当成栈顶却忘了pop_back()后 size 变了,导致越界访问v[v.size()] - 题目要“左边第一个更大”,你却从左往右扫+递减栈——方向反了。正确做法是从右往左扫,或坚持左往右但改用递增栈找“左边更小”
vector 比 stack 更适合调试
std::stack 封装太严,没法遍历、打印中间状态,debug 时抓瞎。生产代码无所谓,刷题或验证逻辑时,直接用 std::vector 模拟更实在。
- 用
vec.push_back(x)代替st.push(x) - 用
vec.back()和vec.pop_back()替代st.top()/st.pop() - 随时
cout ,不用加断点也能看清栈怎么变的
真正卡住的从来不是算法思想,而是下标越界、空栈访问、方向搞反、以及以为自己懂了其实没懂清楚“谁在找谁、往哪边找、用什么序维护”。这些地方一错,整个栈就变成黑盒。










