单调栈

一、单调栈简介

单调栈(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]);
   }

运行结果如图:

 四、总结

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

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

 

posted @ 2023-11-27 18:41  clinx000  阅读(59)  评论(0编辑  收藏  举报