题解:AcWing 830 单调栈
【题目来源】
AcWing:830 单调栈 - AcWing题库
【题目描述】
给定一个长度为 \(N\) 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 -1。
【输入】
第一行包含整数 \(N\),表示数列长度。
第二行包含 N 个整数,表示整数数列。
【输出】
共一行,包含 \(N\) 个整数,其中第 \(i\) 个数表示第 \(i\) 个数的左边第一个比它小的数,如果不存在则输出 \(-1\)。
【输入样例】
5
3 4 2 7 5
【输出样例】
-1 3 -1 2 2
【核心思想】
-
问题分析:给定长度为 \(N\) 的整数数列,对每个元素 \(a_i\),需要找到其左边第一个比它小的元素。若对每个元素向左暴力扫描,时间复杂度为 \(O(N^2)\)。关键观察是:若 \(a_j \geq a_i\) 且 \(j < i\),则 \(a_j\) 不可能是 \(i\) 右侧任何元素的"左边第一个更小值"(因为 \(a_i\) 更近且更小),因此可以维护一个单调递增的候选集合。
-
算法选择:
- 单调栈:维护一个从栈底到栈顶单调递增的元素(或下标)序列,栈顶即为当前元素的左边第一个更小值
-
关键步骤:
- 初始化:空栈,栈顶指针 \(tt = 0\)
- 遍历每个元素 \(a_i\)(\(i\) 从 \(1\) 到 \(N\)):
- 弹出大于等于当前元素的栈顶:
while (tt && a[stk[tt]] >= a[i]) tt--- 这些元素由于值更大且位置更靠左,不可能是后续任何元素的左边第一个更小值
- 获取答案:若栈非空,
ans[i] = a[stk[tt]](栈顶即为左边第一个更小值);否则ans[i] = -1 - 当前元素入栈:
stk[++tt] = i(将当前下标压入栈,成为后续元素的候选)
- 弹出大于等于当前元素的栈顶:
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),每个元素最多入栈出栈各一次
- 空间复杂度:\(O(N)\),栈数组
-
单调栈的核心思想:
- 淘汰不可能最优的元素:新元素 \(a_i\) 入栈时,所有大于等于它的栈顶元素被弹出。这些被弹出的元素值更大且位置更靠左,对于 \(a_i\) 右侧的任何元素,若它们能成为答案,则 \(a_i\) 更近且更小,因此弹出元素永无用处
- 栈顶即答案:由于栈单调递增,栈顶是离当前元素最近且小于它的元素(中间所有大于等于它的元素已被弹出),满足"第一个"和"更小"两个条件
- 均摊线性:每个元素最多被访问两次(入栈一次、出栈一次),因此总时间复杂度为 \(O(N)\)
- 下标存储策略:存储下标而非值,可以同时获取元素值和位置信息,便于处理需要位置信息的扩展问题
- 适用于"左边/右边第一个更大/更小元素"类问题、直方图最大矩形、接雨水等经典场景
【解题思路】

【算法标签】
单调栈
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100010; // 定义数组最大长度
int n; // 输入数字的个数
int stk[N], tt; // stk: 模拟栈的数组, tt: 栈顶指针
int main()
{
cin >> n; // 输入数字个数
for (int i = 0; i < n; i++)
{
int x;
cin >> x; // 输入当前数字
// 维护单调递增栈:弹出所有大于等于当前元素的栈顶元素
while (tt && stk[tt] >= x)
tt--;
// 如果栈不为空,输出栈顶元素(即左边第一个比x小的数)
if (tt)
cout << stk[tt] << " ";
else
cout << -1 << " "; // 栈为空输出-1
// 将当前元素压入栈
stk[++tt] = x;
}
return 0;
}
// 通过洛谷题目,修正模板
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义数组最大长度
int n; // 元素个数
int a[N]; // 存储输入数组
int ans[N]; // 存储结果数组
int stk[N]; // 单调栈(存储下标)
int tt; // 栈顶指针
int main()
{
cin >> n; // 输入元素个数
// 输入数组元素
for (int i = 1; i <= n; i++)
cin >> a[i];
// 使用单调栈处理每个元素
for (int i = 1; i <= n; i++)
{
// 维护单调递增栈:弹出所有大于等于当前元素的栈顶元素
while (tt && a[stk[tt]] >= a[i])
tt--;
// 如果栈不为空,当前元素左边第一个比它小的元素就是栈顶元素
if (tt)
ans[i] = a[stk[tt]];
else
ans[i] = -1; // 栈为空表示左边没有更小的元素
// 将当前元素的下标压入栈
stk[++tt] = i;
}
// 输出结果
for (int i = 1; i <= n; i++)
cout << ans[i] << " ";
cout << endl;
return 0;
}
【运行结果】
5
3 4 2 7 5
-1 3 -1 2 2
浙公网安备 33010602011771号