• AcWing 830.单调栈


    题目传送门

    一、单调栈原理

    单调栈里装的是什么?

    动态维护一个栈,把后续问题的答案都维持在这个栈中,把肯定不再有用的数字从栈中干掉。

    二、理解与感悟

    1、 动态维护,随进随维护,不是预处理。

    2、可以将(O(N^2))的时间复杂度降为(O(N))

    3、此类“左侧(或右侧)最近”,比自己大的(或小的),用单调栈。

    4、算法步骤:
    (1)不管是向左看还是向右看,都先读到数组中。
    (2)代码框架:

    int n;
    stack<int> stk; //装的是序号
    const int N = 1010;
    int a[N];   //a[stk.top()]是指栈顶元素的值
    int res[N]; //装的是序号 a[res[i]]是真正的数值
    
    for(int i=1;i<=n;i++){//从左到右
       while(!stk.empty() && a[stk.top()]>=a[i]) {//栈顶元素找到了右侧第一个比自己小的
          res[stk.top()]=i;
          stk.pop();
      }
      stk.push(i);
    }
    
    for(int i=1;i<=n;i++){
       if(res[i]) cout<<a[res[i]]<<" ";
       else cout<<"-1 ";
    }
    

    三、左边第一个比我小

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n;
    int a[N];
    int stk[N], tt;
    int res[N];//记录“左边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:左边第一个比我小
     6
     6 10 3 7 4 12
    
     结果:
     -1 6 -1 3 3 4
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从右向左遍历每一个数字,不断向左侧进发
        // 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
        for (int i = n; i >= 1; i--) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我小的数字”
            while (tt && a[stk[tt]] > a[i]) {
                res[stk[tt]] = i;//记录下谁是谁的“左边比我小的数字”,然后退群~
                tt--;
            }
            //自己还没有找到“左边比我小的数字”,我还需要继续努力~
            stk[++tt] = i;
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“左边比我小的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    四、左边第一个比我小(STL)

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n;
    int a[N];
    stack<int> stk;
    int res[N];//记录“左边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:左边第一个比我小
     6
     6 10 3 7 4 12
    
     结果:
     -1 6 -1 3 3 4
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从右向左遍历每一个数字,不断向左侧进发
        // 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
        for (int i = n; i >= 1; i--) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我小的数字”
            while (!stk.empty() && a[stk.top()] > a[i]) {
                res[stk.top()] = i;//记录下谁是谁的“左边比我小的数字”,然后退群~
                stk.pop();
            }
            //自己还没有找到“左边比我小的数字”,我还需要继续努力~
            stk.push(i);
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“左边比我小的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    五、左边第一个比我大

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 100010;
    int n;
    int a[N];
    int stk[N], tt;
    int res[N];//记录“左边比我大的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:左边第一个比我大
     6
     6 10 3 7 4 12
    
     结果:
     -1 -1 10 10 7 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从右向左遍历每一个数字,不断向左侧进发
        // 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
        for (int i = n; i >= 1; i--) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我大的数字”
            while (tt && a[stk[tt]] < a[i]) {
                res[stk[tt]] = i;//记录下谁是谁的“左边比我大的数字”,然后退群~
                tt--;
            }
            //自己还没有找到“左边比我大的数字”,我还需要继续努力~
            stk[++tt] = i;
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“左边比我大的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    六、左边第一个比我大(STL)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 100010;
    int n;
    int a[N];
    stack<int> stk;
    int res[N];//记录“左边比我大的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:左边第一个比我大
     6
     6 10 3 7 4 12
    
     结果:
     -1 -1 10 10 7 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从右向左遍历每一个数字,不断向左侧进发
        // 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
        for (int i = n; i >= 1; i--) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我大的数字”
            while (!stk.empty() && a[stk.top()] < a[i]) {
                res[stk.top()] = i;//记录下谁是谁的“左边比我大的数字”,然后退群~
                stk.pop();
            }
            //自己还没有找到“左边比我大的数字”,我还需要继续努力~
            stk.push(i);
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“左边比我大的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    七、右边第一个比我小

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 100010;
    int n;
    int a[N];
    int stk[N], tt;
    int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:右边第一个比我小
     6
     6 10 3 7 4 12
    
     结果:
     3 3 -1 4 -1 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从左向右遍历每一个数字
        for (int i = 1; i <= n; i++) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“右边比我小的数字”
            while (tt && a[stk[tt]] > a[i]) {
                res[stk[tt]] = i;//记录下谁是谁的“右边比我小的数字”,然后退群~
                tt--;
            }
            //自己还没有找到“右边比我小的数字”,我还需要继续努力~
            stk[++tt] = i;
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“右边比我小的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    八、右边第一个比我小(STL)

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n;
    int a[N];
    stack<int> stk;
    int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     右边第一个比我小
     测试用例:
     6
     6 10 3 7 4 12
    
     结果:
     3 3 -1 4 -1 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从左向右遍历每一个数字
        for (int i = 1; i <= n; i++) {
            //如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“右边比我小的数字”
            while (!stk.empty() && a[stk.top()] > a[i]) {
                res[stk.top()] = i;//记录下谁是谁的“右边比我小的数字”,然后退群~
                stk.pop();
            }
            //自己还没有找到“右边比我小的数字”,我还需要继续努力~
            stk.push(i);
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“右边比我小的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    九、右边第一个比我大

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n;
    int a[N];
    int stk[N], tt;
    int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:右边第一个比我大
     6
     6 10 3 7 4 12
    
     结果:
     10 12 7 12 12 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从左向右遍历每一个数字
        for (int i = 1; i <= n; i++) {
            //如果栈不空,并且栈顶元素小于遍历到的数字,那么这些数字就是已经找到了“右边比我大的数字”
            while (tt && a[stk[tt]] < a[i]) {
                res[stk[tt]] = i;//记录下谁是谁的“右边比我大的数字”,然后退群~
                tt--;
            }
            //自己还没有找到“右边比我大的数字”,我还需要继续努力~
            stk[++tt] = i;
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“右边比我大的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    十、右边第一个比我大(STL)

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 100010;
    int n;
    int a[N];
    stack<int> stk;
    int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
    
    /**
     测试用例:右边第一个比我大
     6
     6 10 3 7 4 12
    
     结果:
     10 12 7 12 12 -1
     */
    int main() {
        //读入到数组,安全又健康
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        //从左向右遍历每一个数字
        for (int i = 1; i <= n; i++) {
            //如果栈不空,并且栈顶元素小于遍历到的数字,那么这些数字就是已经找到了“右边比我大的数字”
            while (!stk.empty() && a[stk.top()] < a[i]) {
                res[stk.top()] = i;//记录下谁是谁的“右边比我大的数字”,然后退群~
                stk.pop();
            }
            //自己还没有找到“右边比我大的数字”,我还需要继续努力~
            stk.push(i);
        }
        //正序输出结果
        for (int i = 1; i <= n; i++) {
            //如果找到了它的“右边比我大的数字”,那么res[i]>0,否则就是没有找到
            if (res[i]) cout << a[res[i]] << ' ';
            else cout << "-1 ";
        }
        return 0;
    }
    

    十一、视频讲解

    https://www.ixigua.com/6925435220292436494

  • 相关阅读:
    精通搜索分析
    简析MonoTouch工作原理
    第二次结对编程作业
    第02组 团队Git现场编程实战
    第一次结对编程作业
    团队项目需求分析报告
    第一次个人编程作业
    软工第一次作业
    团队项目选题报告
    Rookey.Frame之数据库及缓存配置
  • 原文地址:https://www.cnblogs.com/littlehb/p/15247256.html
Copyright © 2020-2023  润新知