• 单调栈和单调队列入门


    单调栈是什么?

    分为单调递增和单调递减栈。(栈内元素成递增或者递减性)

    例如:
    当前单调递增栈内元素[1,2,4],之后入栈的元素为(3),
    为了维护单调性,元素(4)出栈

    [[1,2,4]-入栈(3) -出栈(4)- [1,2,3]-入栈(0)-出栈(1)(2)(3)-[0] ]

    单调递增栈主要作用:

    把序列中每个元素放到单调栈中进行维护就可以在(O(n))时间复杂度内
    求出区间每个元素为最大值/最小值时,影响的区间范围为[left,right]。

    单调递增↗栈求最小值影响范围

    单调递减↘栈求最大值影响范围

    (例如:序列{1,2,3,2,1})

        1 2 3 2 1
            口
          口口口
        口口口口口
        0 1 2 3 4
    

    用单调递减栈即可求出

    最大值 区间[left,right]
    1 [0,0]
    2 [0,1]
    3 [0,4]
    2 [3,4]
    1 [4,4]

    维护单调栈:

    这里我们以单调递增栈为例,求最小值影响范围

    我们规定将下标(index)压入栈中。为了方便编码,我们在使用单调栈的数组的最后加入-INF(一个极小值),方便后续的出栈。

    序列变成 ({1,2,3,2,1,-INF})

    i 要入栈的height[i] 栈的变动 变动后的栈
    0 1 push(0) [0]
    1 2 push(1) [0,1]
    2 3 push(2) [0,1,2]
    3 2 pop(2),push(3) [0,1,3]
    4 1 pop(3),pop(1),push(4) [0,4]
    5 -INF pop(0),push(4) []

    [left,right]中的right:

    若元素height[i]从栈中pop出就说明这个元素为最小值的右侧影响范围到此为止。

    [left,right]中的left:

    因为栈内元素单调递增,栈pop之后,栈顶的元素height[s.top()]不大于pop的元素。所以左侧的影响范围为pop之后栈顶的元素的下标+1,这里需要注意pop之后栈为空的情况,因为pop之后栈为空,说明没有元素是比pop出的这个元素小,那这个pop出的元素影响它左端的所有元素。

    //单调递增栈
    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long int LL;
    const int MAXN = 1e6 + 1;
    LL height[MAXN];
    int N;
    
    void solve(){
    	height[N] = -INF;
    	stack<int> s;
    	for(int i=0;i<=N;i++){
    		while(!s.empty() && height[s.top()] > height[i]){
    			int cur = s.top();
    			s.pop();
    			int _left = s.empty()?0:s.top()+1;
    			int _right = i-1;
    			cout << height[cur] << " " << _left << " " << _right << endl;
    		}
    		s.push(i);
    	}
    }
    
    int main() {
    	cin >> N;
    	for(int i=0;i<N;i++) cin >> height[i];
    	solve();
    	return 0;
    }
    
    //单调递减栈
    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long int LL;
    const int MAXN = 1e6 + 1;
    LL height[MAXN];
    int N;
    
    void solve(){
    	height[N] = INF;
    	stack<int> s;
    	for(int i=0;i<=N;i++){
    		while(!s.empty() && height[s.top()] < height[i]){
    			int cur = s.top();
    			s.pop();
    			int _left = s.empty()?0:s.top()+1;
    			int _right = i-1;
    			cout << height[cur] << " " << _left << " " << _right << endl;
    		}
    		s.push(i);
    	}
    }
    
    int main() {
    	cin >> N;
    	for(int i=0;i<N;i++) cin >> height[i];
    	solve();
    	return 0;
    }
    

    单调栈模板

    void solve(){
        //单调递增栈 -INF,递减 INF
    	height[N] = -INF;
    	stack<int> s;
    	for(int i=0;i<=N;i++){
            //单调递增栈 >,递减 <,等号看题目
    		while(!s.empty() && height[s.top()] > height[i]){
    			int cur = s.top();
    			s.pop();
    			int _left = s.empty()?0:s.top()+1;
    			int _right = i-1;
    			cout << height[cur] << " " << _left << " " << _right << endl;
    		}
    		s.push(i);
    	}
    }
    

    单调队列

    引入双端队列的概念。
    元素可以从队列的头部和尾部进行插入和删除。

    那么单调队列和单调栈的区别在于栈与双端队列的区别,在原有单调栈的基础上,你可以修改和获取到栈底的元素,这就导致了你可以对最值影响区间[Left,Right]中的Left进行控制,并且可以直接获得这个区间最值是多少。(原本因为栈顶元素未知,所以无法获取),也就是说可以 (O(n))求整个序列中,区间长度为k的区间最值

    //输出区间[left,right],长度为m的最小值.
    inline void solve(){
    	deque<int> q;
    	for(int i=0;i<n;i++) {
    		//单调递增栈 >,递减 <
    		while(!q.empty()&&height[q.back()]>height[i]) q.pop_back();
                    //以下根据题意进行更改
    		printf(i>0?"0
    ":"%d
    ",height[q.front()]);
    		q.push_back(i);
    		if(i-q.front()>=m) q.pop_front();//限制区间长度为m
    	}
    	putchar('
    ');
    }
    

    题目

    P1886 滑动窗口

    P1440 求m区间内最小值

  • 相关阅读:
    python列表(包含列表方法)
    python数据类型和运算符
    python计算机初识
    python运行过程,变量,符号
    python·if语句
    python`while循环
    <select></select> php表单怎么传值
    thinkPhp3 空操作
    thinkphp3.1和3.2的<模板替换>的区别
    thinkphp3 空操作 如果为空会怎么样 empty name=""
  • 原文地址:https://www.cnblogs.com/--zz/p/11247874.html
Copyright © 2020-2023  润新知