题目展示区
可见 acwing.830
题目描述:
给定一个长度为 N 的整数数列, 输出每个数左边第一个比它小的数, 如果不存在则输
出 -1。
输入格式:
第一行包含整数 N, 表示数列长度
第二行包含 N 个整数,表示整数数列
输出格式:
共一行, 包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不
存在则输出 -1。
数据范围:
1 ≤ N ≤ , 1 ≤ 数列中元素 ≤
输入样例:
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()
如果说是算法题的,考虑题目中的数据范围,会超时的对吧,所以这个普通做法这个
是行不通
优化做法:
方案:
还是开一个数组,开两个也行,但是开一个就够用了,开一个,边读入边输出。
维护一个单调栈的结构,怎么维护呢,开一个数组,名为 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() 就快了好多了。
样例模拟区
当前栈中元素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