数组模拟数据结构 —— 单调栈

题目展示区

        可见 acwing.830

        题目描述:

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

        出 -1。

        输入格式:

                第一行包含整数 N, 表示数列长度

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

        输出格式:

                共一行, 包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不

        存在则输出 -1。

        数据范围:

                1 ≤ N ≤ 10^{5} , 1 ≤ 数列中元素 ≤ 10^{9}

        输入样例:
5
3 4 2 7 5
        输出样例:
-1 3 -1 2 2

概念介绍区

        栈:

                是一种数据结构,可这样想象,形如一根管子,只有一端开口,物品只能从一端进, 也

        只能从这一端出, 所以具有 “先进后出,后进先出的” 特点。

        单调栈:

                本题目中所维护的栈中元素,具有单调性(一个有序序列, 从左往右依次递增或不减, 

        或者从左往右依次递减或不增,都可以成为单调性), 所以叫单调栈

题目分析区

        普通做法:
                方案:

                普通做法的话,我们可以先给这个整数数列存下来,然后从左往右依次遍历每个元素,

        我们可以先定义一个整数数组,然后从下标为 1 的位置开始存,比方说现在我们遍历到第 i

        个数,那么此时我们可以设置一个指针 j , 初始时 指向 i - 1 如果说 j 指向的这个元素比当前

        i 指向的元素小的话,就输出,然后继续往下遍历第 i + 1 个元素就好了, 如果不比 i 指向的

        这个元素小的话,j 就往左遍历第 j - 1 个元素,如果 j 变成 0 的时候,还没有找到比 第 i 个

        元素小的数,就 输出 -1, 下面画了个图

        

               时间复杂度:

                 可以想象一种情况啊, 如果说给我们的这个整数数列是单调递减的,那么别管遍历每个

        数,j 指针都会从 i 遍历到 最左边的, 这种情况下时间复杂度是最高的,大概就是

                O(1 + 2 + 3 + ······ + n - 1 + n)    ≈    O(n^{2}

                如果说是算法题的,考虑题目中的数据范围,会超时的对吧,所以这个普通做法这个

        是行不通

        

        优化做法:
                方案:

                还是开一个数组,开两个也行,但是开一个就够用了,开一个,边读入边输出。

                维护一个单调栈的结构,怎么维护呢,开一个数组,名为 stk ,栈顶指针为 top,初始

        时栈中没有元素,也就是 top 指针为 0。

                假设现在栈中已经有了一些元素,并且现在这个栈中的元素,从左往右具有单调性

     (关于是单调递增, 还是单调递减, 我们稍后讨论), 假设我们现在讨论到了数列中的某个

        元素 a, 那么怎么做才能将 a 加到栈中,并且还能维护这么个单调性嘞。

                首先,我们先拿出当前这个栈顶元素跟 a 进行比较,如果说,当前栈顶元素小于 a, 那

        么栈顶元素,根据题目要求,就是我们要找的那个元素对吧,直接输出就好了,然后再让 top

        指针先自增一下,然后再将 a 加到栈顶, 如果说,当前栈顶元素大于等于 a,那么这个栈顶

        元素是不符合题意滴,我们直接让它出栈就好了,也就是直接让 top 指针减一就行了(由

        于是模拟的数据结构,开的是一个连续的数组,没有释放空间的必要),如果说top 减一

        之后,也就是新的栈顶元素也是比当前元素 a 大或者相等的话,我们接着让 top 减一,top

        最多减到什么程度嘞,最多减到 0, 也就是栈为空的时候,如果说减到中间某个位置,当

        前的栈顶元素小于 a,那么当前这个栈顶元素符合题意,就输出就好了,然后再给 top 自增

        一 下,将 a 加进去。

                当然,在输出之前, 是需要特判一下滴,就是看当前 栈顶指针 top 是否为 0, 为 0 的

        话呢,就输出 -1, 不为 0 的话,就输出栈顶元素,这样,我们维护的就是一个单调递增的

        单调栈。

                时间复杂度:

                这么做的话,我们几乎就是在 O(1) 时间内找到了答案,那么最多 n 个元素,所以整体

        这个时间复杂度就是 O(n) 级别的,相较于普通做法的 O(n^{2}) 就快了好多了。

样例模拟区

当前栈中元素stk[] : 
当前元素 :3
top 指针指向 :0
输出结果 :-1

当前栈中元素stk[] : 3 
当前元素 :4
top 指针指向 :1
输出结果 :3

当前栈中元素stk[] : 3 4 
当前元素 :2
top 指针指向 :2
输出结果 :-1

当前栈中元素stk[] : 2 
当前元素 :7
top 指针指向 :1
输出结果 :2

当前栈中元素stk[] : 2 7 
当前元素 :5
top 指针指向 :2
输出结果 :2

                                 

代码 + 注释区

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int stk[N]; // 开一个长度为 N 的数组

int main(){
    // 读入总的元素个数
    cin >> n;
    
    // 定义一个栈顶指针
    int top = 0;
    for (int i = 1; i <= n; i ++){
        // 从头到尾,边读边处理这些元素
        int a;
        cin >> a;
        
        // 当栈顶指针不为 0 和 栈顶元素大于当前元素时
        // 让栈顶元素弹出
        while (top && stk[top] >= a) top --;
        
        // 判断一下栈是否为空、
        // 空的话就表示栈中没有元素比 栈 中元素小
        if (!top) cout << -1 << ' ';
        else cout << stk[top] << ' ';
        
        // 栈顶指针先自增,再给当前元素加进去
        stk[ ++ top] = a;
    }
    
    return 0;
}

学习网站推荐

        www.acwing.com   www.bilibili.com

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值