题解:AcWing 830 单调栈

【题目来源】

AcWing:830 单调栈 - AcWing题库

【题目描述】

给定一个长度为 \(N\) 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 -1。

【输入】

第一行包含整数 \(N\),表示数列长度。

第二行包含 N 个整数,表示整数数列。

【输出】

共一行,包含 \(N\) 个整数,其中第 \(i\) 个数表示第 \(i\) 个数的左边第一个比它小的数,如果不存在则输出 \(-1\)

【输入样例】

5
3 4 2 7 5

【输出样例】

-1 3 -1 2 2

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的整数数列,对每个元素 \(a_i\),需要找到其左边第一个比它小的元素。若对每个元素向左暴力扫描,时间复杂度为 \(O(N^2)\)。关键观察是:若 \(a_j \geq a_i\)\(j < i\),则 \(a_j\) 不可能是 \(i\) 右侧任何元素的"左边第一个更小值"(因为 \(a_i\) 更近且更小),因此可以维护一个单调递增的候选集合

  2. 算法选择

    • 单调栈:维护一个从栈底到栈顶单调递增的元素(或下标)序列,栈顶即为当前元素的左边第一个更小值
  3. 关键步骤

    • 初始化:空栈,栈顶指针 \(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(将当前下标压入栈,成为后续元素的候选)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N)\),每个元素最多入栈出栈各一次
    • 空间复杂度:\(O(N)\),栈数组
  5. 单调栈的核心思想

    • 淘汰不可能最优的元素:新元素 \(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
posted @ 2026-07-02 22:46  团爸讲算法  阅读(10)  评论(0)    收藏  举报