单调栈 单调队列

单调栈

单调栈:栈中数据从栈底至栈顶具有单调性。要求遍历的序列中每个元素都必须要进入一次单调栈。由于其单调性,不要求每个元素在遍历序列之后仍然保留在单调栈内。

应用:求解 N G E / N L E 、 P G E / P L E NGE/NLE、PGE/PLE NGE/NLEPGE/PLE问题( N e x t / P r e v i o u s   G r e a t e r / L e s s   E l e m e n t Next/Previous\ Greater/Less\ Element Next/Previous Greater/Less Element)。

  • G r e a t e r Greater Greater类问题:构造单调递减
    L e s s Less Less类问题:构造单调递增
  • P r e v i o u s Previous Previous类问题:以即将入栈的元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
    N e x t Next Next​类问题:以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同

N G E / N L E NGE/NLE NGE/NLE问题

  • N G E NGE NGE:构造单调递减栈,以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同

  • N L E NLE NLE:构造单调递增栈,以栈顶元素为视角看即将入栈元素,允许即将入栈元素与栈顶元素相同

//tuple三个数据分别为:data:元素本身 index:元素所在下标 ans:答案所在下标
extern vector<tuple<int,int,int>>v;
extern stack<tuple<int,int,int>>s;
void nge(){
    for(int i=0;i<v.size();i++){
        if(s.empty()||get<0>(s.top())>=get<0>(v[i])){//nle为<= 允许栈顶元素和即将入栈元素相同
            s.push(v[i]);
        }else{
            while(s.size()&&get<0>(s.top())<get<0>(v[i])){//nle为>
                get<2>(v[get<1>(s.top())])=get<1>(v[i]);
                s.pop();
            }
            s.push(v[i]);
        }
    }
}

P G E / P L E PGE/PLE PGE/PLE问题

  • P G E PGE PGE:构造单调递减栈,以入栈元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
  • P L E PLE PLE:构造单调递增栈,以入栈元素为视角看栈顶元素,不允许栈顶元素与即将入栈元素相同(入栈前必须将栈中与其相同元素全部岀栈)
//tuple三个数据分别为:data:元素本身 index:元素所在下标 ans:答案所在下标
extern vector<tuple<int,int,int>>v;
extern stack<tuple<int,int,int>>s;
void pge(){
    for(int i=0;i<v.size();i++){
        if(s.empty()){
            s.push(v[i]);
        }else if(get<0>(v[i])<=get<0>(s.top())){//ple为>=
            while(get<0>(v[i])==get<0>(s.top())) s.pop();//不允许栈顶元素与即将入栈元素相同
            get<2>(v[i])=get<1>(s.top());
            s.push(v[i]);
        }else{
           while(!s.empty()&&get<0>(v[i])>get<0>(s.top())) s.pop();//ple为<
            get<2>(v[i])=get<1>(s.top());
           s.push(v[i]);
        }
    }
}

单调队列

单调队列:队列中数据从队头到队尾具有单调性。要求遍历的序列中每个元素都必须要进入一次单调队列。由于其单调性,不要求每个元素在遍历序列之后仍然保留在单调队列内。

由于需要对队列进行队尾入队,队头、队尾出队,故采用双端队列(deque)实现。

应用:滑动窗口最值问题、最大子序和问题

滑动窗口最值问题

滑动窗口利用双指针的思想,是尺取法的应用。用单调队列可解决滑动窗口的最值问题。

  • 最大值问题:维护单调递减队列
  • 最小值问题:维护单调递增队列

关键操作:

  1. 删头:若队头元素脱离窗口(超过窗口范围),将队头元素从队头出队
  2. 去尾:若新元素在从队尾入队时,原队尾破坏了队列的单调性,使原队尾出队(因为必须保证每个元素都能入队,由于滑动窗口的滑动特性,不要求每个元素都持续在队列中)
extern int n,k;//n:序列长 k:窗口长
//data index
extern vector<pair<int,int>>a;
extern deque<pair<int,int>>m;
void solve(){
    //单增队列
    for(int i=0;i<n;i++){
        while(!m.empty()&&m.back()>a[i]) m.pop_back();//<为单减队列
        m.push_back(a[i]);
        if(i>=k-1){//窗口大小已取足
            while(!m.empty()&&get<1>(m.back())-get<1>(m.front())>=k) m.pop_front();
            cout<<get<0>(m.front())<<' ';
        }
    }
}

最大子序和问题

(未完待续)

  • 21
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值