首页 > 其他分享 >单调栈

单调栈

时间:2023-11-27 18:44:06浏览次数:23  
标签:遍历 nums 元素 num 单调 empty

一、单调栈简介

单调栈(Monotone Stack),拆分一下“单调”,“栈”。也就是说它是在栈的基础之上在多加了一条“单调”的性质。

一般来说有单调增加,单调递减两种方式,也就是说从栈顶栈底,里面的元素是按照一定顺序来排列的。

它的时间复杂度为O(n)。

二、单调递增栈

只有比栈顶元素小的元素才能够被直接压入栈,否则就要先出再入。因为栈是先进后出的,所以在我们按照要求遍历完之后

我们这个栈中的栈顶的元素一定是这个栈中最小的元素,并且栈中元素大小是依次递增的。接下来我们理解什么情况入栈和出栈。

  • 对于当前的元素假设为num,如果它比栈顶的元素小,按照我们的定义就需要将它入栈。
  • 当num比栈顶的元素大的时候,我们开始遍历这个栈,在这个过程中不断地出栈,直到找到一个比它大的元素。在遍历过程中,有可能一直出栈然后导致栈为空,这是正常的。

代码演示:

   stack<int> s;
   vector<int> nums = {2, 5, 3, 7, 4, 6, 9, 1};
   for(int num : nums){//开始遍历这个数组
    while(!s.empty() && num > s.top()){
    /*
    如果当前的num大于栈顶的元素,开始遍历栈然后进行出栈,直到找到第一个小于它的元素
    */
    
        s.pop();
    }
    //入栈
    s.push(num);
   }

接下来我们将一下单调递增栈的使用场景

1.寻找左侧中第一个比当前元素大的元素

  • 当我们从左往右遍历数组的过程中,如果左侧存在比目标值大的元素,那么此时栈顶的元素就是我们要找的值。
  • 如果发现栈是空的,就说明左侧没有比它大的元素。

于是我们将上面的代码更改为如下所示:

   stack<int> s;
   vector<int> nums = {2, 5, 3, 7, 4, 6, 9, 1};
   for (int num : nums){ // 开始遍历这个数组
    while (!s.empty() && num > s.top()){
           /*
           如果当前的num大于栈顶的元素,开始遍历栈然后进行出栈,直到找到第一个小于或者等于它的元素
           */
           s.pop();
       }
    if(!s.empty())
        cout << num << "的左侧第一个比它大的元素是" << s.top() << endl;
    else
        cout << num << "的左侧没有比它大的元素" << endl;
    // 入栈
        s.push(num);
   }

运行结果如图:

2.寻找右侧中第一个比当前元素大的元素

  • 当我们从右往左遍历数组的过程中,如果右侧存在比目标值大的元素,那么此时栈顶的元素就是我们要找的值。
  • 同上,栈为空代表没有

代码演示:

   stack<int> s;
   vector<int> nums = {2, 5, 3, 7, 4, 6, 9, 1};
   for (int i = nums.size() - 1; i >= 0; i--){
    while (!s.empty()&& nums[i] > s.top())
    {
        s.pop();
    }
    if(!s.empty()){
        cout << nums[i] << "的右侧第一个比它大的元素是" << s.top() << endl;
    }
    else
        cout << nums[i] << "的右侧没有比它大的元素" << endl;

    s.push(nums[i]);
   }

运行结果如图:

 

三、单调递减栈

与单调递增栈类似,不同点在于只有比栈顶元素大的元素才能够被直接压入栈,否则就要先出再入。

完成遍历后,栈顶的元素是最大的然后依次递减直到栈底。

  • 对于当前元素num,如果比栈顶的元素大就直接压入栈,成为新的栈顶。
  • 否则就要不断地出栈,直到找到一个比他小的元素再入栈。

 代码演示:

   stack<int> s;
   vector<int> nums = {2, 5, 3, 4, 9, 7, 6};
   for(int num : nums){
    while(!s.empty() && num < s.top()){
        s.pop();
    }
    s.push(num);
   }

接下来我们介绍一下单调递减栈的使用

1.寻找左侧中第一个比当前元素小的元素

它的遍历类似于之前在单调递增栈中的模式。在从左往右遍历的过程中如果存在要找的值,那么栈顶元素就是我们要找的值

代码演示

   stack<int> s;
   vector<int> nums = {2, 5, 3, 4, 9, 7, 6};
   for(int num : nums){
    while(!s.empty() && num < s.top()){
        s.pop();
    }
    if(!s.empty()){
        cout << num << "的左侧第一个比它小的元素是" << s.top() << endl;
    }
    else
        cout << "左侧没有比它小的元素" << endl;
    s.push(num);
   }

运行结果如图:

2.寻找右侧中第一个比当前元素小的元素

直接就代码演示了:



   stack<int> s;
   vector<int> nums = {2, 5, 3, 4, 9, 7, 6};

for (int i = nums.size() - 1; i >= 0; i--){
    while (!s.empty()&& nums[i] < s.top())
    {
        s.pop();
    }
    if(!s.empty()){
        cout << nums[i] << "的右侧第一个比它小的元素是" << s.top() << endl;
    }
    else
        cout << nums[i] << "的右侧没有比它小的元素" << endl;

    s.push(nums[i]);
   }

运行结果如图:

 四、总结

单调栈大多用于处理求某一个元素的左边或者右边中第一个比它大或者比它小的元素的问题。

查找 「比当前元素大的元素」 就用 单调递增栈,查找 「比当前元素小的元素」 就用 单调递减栈。

 

标签:遍历,nums,元素,num,单调,empty
From: https://www.cnblogs.com/linx000/p/17859034.html

相关文章

  • 907. 子数组的最小值之和(贡献法,单调栈,前后缀分解)
     题目不难,但是涉及到的知识点很丰富。classSolution:defsumSubarrayMins(self,arr:List[int])->int:MOD=10**9+7n=len(arr)pre=[-1]*nsuf=[n]*nstk=[]foriinrange(n):w......
  • 队列(最基本队列,标准队列 2个,双端队列,单调队列)
    2023-11-26最基本队列:一次性使用的classQueue01{//最基本队列,一次性的,数组模拟,先进先出//功能:入队,出队,判满,判空,显示队头,显示队列privateint[]queue;privateintfront=-1;//指向第一个元素前一个位置privateinttail=-1;//指向最后一个元素p......
  • 2023.11.26 单调栈与字符串
    cf上的1886C从第一个字符开始往后,删除第一对第一个字符大于第二个字符的相邻字符组中的第一个字符。还没找到就一直入栈,当即将入栈元素和栈顶元素满足上述条件时,栈顶元素出栈,继续判断,直到待入元素满足入栈条件。(每一次有元素出栈,要执行一次查询位置减字符串长度,字符串长度减一) ......
  • 推箱子保证单调性的正确性
    如果保证了单调性,那么一个状态在出队的时候,一定是这个状态的最优情况反证,如果不是最优情况,那么肯定存在一个状态A,A能到达这个状态且会让这个状态变优由于这个状态变优了,要么就是箱子移动的步数少了,要么就是箱子移动的步数是一样的但人移动的步数少了然后这个更优的状态是由A移......
  • 【题目-理想的正方形】 二维单调队列
    理想的正方形(二维单调队列)题目acwing.1091理想的正方形题解题目很好做,主要学习一下二维单调队列的写法首先将每行各窗口内最值用单调队列维护出来,保存在rmax中接着对rmax各列,将每列最值用单调队列维护出来,保存在cmax中,最后cmax中存的就是行和列窗口乘积范围的二维区间......
  • 单调队列优化多重背包
    多重背包题目已经很熟了我们要把它优化到O(nm)也就是对于每一个物品,我们只能够对dp数组进行一次遍历,并且不能枚举取几个物品或者说是,要在每一个状态下O(1)的找到取不同数量物品的最优解,并转移我们可以发现,其实转移的区间是非常有规律的,f[j]只能够从f[j-v[i]],f[j-2*v[i]]....f[j-c[i]*v......
  • 单调栈模型
    单调栈本质:及时去掉无用数据,保证栈中数据有序。 模板题: classSolution:defdailyTemperatures(self,temperatures:List[int])->List[int]:n=len(temperatures)stk=[]ans=[0]*nforiinrange(n-1,-1,-1):......
  • 代码随想训练营第三十七天(Python)| 738.单调递增的数字、968.监控二叉树
    738.单调递增的数字classSolution:defmonotoneIncreasingDigits(self,n:int)->int:#主要思路当前数字比前面数字小时。前面数字-1,当前数字变2为9str_n=str(n)foriinrange(len(str_n)-1,0,-1):ifstr_n[i]<str_n[......
  • 单调栈
    单调栈定义单调栈即满足单调性的栈结构。与单调队列相比,其只在一端进行进出。使用方法:就是从栈顶读出来一个元素,该元素满足单调性的某一端。例如取出栈中的最小值。原理将一个元素插入单调栈时,为了维护栈的单调性,需要在保证将该元素插入到栈顶后整个栈满足单调性的前提下弹出......
  • 单调队列
    acwing154滑动窗口,单调队列q存的是下标,真正的值需要再套一个a数组1#include<iostream>2usingnamespacestd;34constintN=1e6+10;56intn,k;7inta[N],q[N];//q代表单调队列89intmain()10{11scanf("%d%d",&n,&k);12for(in......