单调栈---基础数据结构与算法

简介

栈 (stack) 又名堆栈,是一种数据结构,向一个栈插入新元素又称作进栈、入栈或压栈,从一个栈删除元素又称作出栈或退栈。

栈是一种只允许在表尾进行插入和删除操作的线性表,也就是我们所说的后进先出,我们把栈想象成往一个有底的桶中放铁饼,你从桶中拿铁饼,只能拿到最上边的,放铁饼也只能在最上边开始放,如图

单调栈---基础数据结构与算法_第1张图片

栈的实现分两种,数组模拟和链表实现,这里用数组模拟

栈的数组模拟

如果学过了链表,那就对栈的实现很容易上手,甚至只需要看一眼就能够领悟。

const int N =100010;
int stk[N],tt; //tt是栈顶的下标

//栈的插入操作 下标从1开始被使用
stk[++tt] = x ; 

//栈的弹出操作
--tt;

//判断栈是否为空
if(tt>0) not empty //不为空
else empty //为空

//取栈顶元素
stk[tt];

 这里大概了解了栈,那单调栈只不过是栈内元素呈单调性的栈,可能是单调递增,也可能是单调递减。

栈的主要应用场景:找出一个序列中每个数左/右边离它最近的比它大/小的数。

这里来一道题目,快速上手>

题目

给定一个长度为 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

输出解释:

3的左边没有比它小的数 输出-1

4的左边比它小的数是3 输出3

2的左边没有比它小的数 输出-1

7的左边比它小的数并且最近的是2 输出2

5的左边比它小的数并且最近的是2 输出2

暴力解法

我们首先想到的暴力算法是,我们要求第 i 个数左边比它小的数并且离它最近,那就是从 i-1 个数开始,向左边遍历,直到找到。

那代码应该这么写

for(int i = 0 ; i < n ; i++ )
{
    for( int j = i - 1 ; j >= 0 ; j-- )
    { 
        if( a[j] <= a[i] )
        {
            printf("%d ",a[j]);
            break;
        }
    }
    //
}

我们观察,暴力做法中可以有什么性质可以减少步骤?

如果有一个数的左边某个数 x 比它还大,那么在这题中 这个x永远都不会被考虑到

例如 a[x]  >=  a[y] 并且 x< y 那么 a[x] 就可以被删去,如图

单调栈---基础数据结构与算法_第2张图片

图中存在这样逆序的点,当我们将所有逆序的点删除了,就得到了

单调栈---基础数据结构与算法_第3张图片

这时,我们只需要将我们的 a[i] 依次与 stk[4]、stk[3]、stk[2] 、stk[1]进行比较(找到小于a[i]的)就可以得出答案

好,这里看代码领悟一下

单调栈解法

#include 
using namespace std;

const int N = 100010;
int stk[N], tt ;

int main() 
{
    int n;
    scanf("%d", &n);
    while (n--) 
    {
        int x;
        scanf("%d", &x);
        while (tt && stk[tt] >= x) tt--; //当栈中存在元素并且栈顶元素大于等于x时,从栈中弹出栈顶元素
        if (tt) printf("%d ", stk[tt]);//当存在栈顶元素小于x时,将此栈顶元素输出
        else printf("-1 ");  //当未在栈中查找到小于x的元素时,栈中所有元素均被弹出,此时栈为空,输出-1
        stk[++tt] = x; //将x插入到栈顶 
    }

    return 0;
}

你可能感兴趣的:(数据结构,算法,c++)