zoukankan      html  css  js  c++  java
  • 单调栈&单调队列

    单调队列&单调栈

    感觉我有限的智力难以理解,因此写下来研究一下(

    单调队列

    顾名思义,单调队列就是一个队列内容有单调性的队列(

    我们可以用单调队列来维护一列数据中,某长度段连续的子区间中的最大/最小值

    例如,给定一串数字,求其每个长度为m的连续子区间的最小值

    相较于ST表,线段树等数据结构,单调队列均摊复杂度O(n),会快很多

    以下以一个递减的单调队列为例,简单描述一下如何构造单调队列:

    在区间往后移动了一位后,我们假定新出现的数字为A

    1. 单调队列的队首是以前加入的,那么它可能不在新的区间中,进行判断是否需要将其删除
    2. 如果A比队尾的数字小,没有破坏单调性,我们可以直接把A加入队尾
    3. 如果A比队尾的数字大,因为A是新加入的数字,它可能在未来的某个区间中成为区间最大值,而在队中,且比A小的数字一定存在于包含A的区间中,且没有A大,所以没用删掉完事

    这样我们就构造出了一个单调队列


    以下代码 洛谷:P1886 滑动窗口 /【模板】单调队列

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include<vector>
    #include<iomanip>
    #include<map>
    #include<queue>
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> P;
    int n, k;
    struct node
    {
    	int id,  val;
    	node(int x=0, int y=0) { id = x, val = y; };
    }que[1000000+5],que2[1000000+5];
    int dataa[2000000];
    int ans1[1000000+5],ans2[1000000+5];
    int main() {
    	cin >> n >> k;
    	for (int i = 0; i < n; i++)scanf("%d", &dataa[i]);
    	int l1 = 0, r1 = -1, l2 = 0 ,r2=-1;						//初始化队头队尾指针
    	for (int i = 0; i < n; i++) {
    		node now = node(i, dataa[i]);
    		//单调减队列
    		while (l1 <= r1 && que[l1].id < now.id - k + 1)l1++;  //去头
    		while (l1 <= r1 && que[r1].val < now.val)r1--;		 //去尾
    		que[++r1] = now;
    		ans1[i] = que[l1].val;							   //ans[i]储存以i为结尾的区间内的极值
    		//单调增队列
    		while (l2 <= r2 && que2[l2].id < now.id - k + 1)l2++;
    		while (l2 <= r2 && que2[r2].val > now.val)r2--;
    		que2[++r2] = now;
    		ans2[i] = que2[l2].val;
    	}
    	for (int i = k - 1; i < n; i++)cout << ans2[i] << ' ';
    	cout << "
    ";
    	for (int i = k - 1; i < n; i++)cout << ans1[i] << ' ';
    }
    

    单调栈

    单调栈就是栈内元素出栈序列单调有序的栈

    没了

    要实现单调栈,我们只需要进行一下操作(以下操作是单调递增栈,即出栈序列递增)

    1. 我们要想令一个值A入栈时,首先检查栈顶元素是否大于A
    2. 栈顶元素大于等于A,A可以直接入栈
    3. 栈顶元素小于A,一直将栈顶元素出栈,直到栈顶元素大于A,随后A入栈

    PS.我们构造单调栈时,栈内储存的是元素的下标,而不是元素的值

    那么这玩意有啥用呢(

    使用单调栈,我们可以解决诸如寻找数列中某个数之前/后第一个大于/小于它的数的问题

    正确性:

    我们以寻找某个数前,第一个大于他的数为例

    构造一个单调递增的栈,设当前数为A,此时

    • 栈顶元素一定是在A之前就出现的数
    • 栈顶元素一定是A的前一个数,如果A比栈顶元素小,那么A之前第一个大于它的数肯定是栈顶
    • 如果A大于栈顶元素,栈顶元素出栈,现在栈顶就是前栈顶之前第一个大于它的数,以此类推,当不能再出栈后,栈顶即为正解

    以下代码:洛谷:P5788 【模板】单调栈

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include<vector>
    #include<iomanip>
    #include<map>
    #include<queue>
    #include<stack>
    #define lc(i) (2*i+1)
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> P;
    int n, k;
    stack<long long>sk;
    ll dataa[3000000 + 5];
    ll ans[3000000 + 5];
    int main() {
    	cin >> n;
    	for (int i = 0; i < n; i++)scanf("%d",&dataa[i]);
    	for (int i = n - 1; i >= 0; i--) {
    		while (!sk.empty() && dataa[sk.top()] <= dataa[i])sk.pop();
    		if (sk.empty())ans[i] = -1;
    		else ans[i] = sk. top();
    		sk.push(i);
    	}
    	for (int i = 0; i < n; i++)cout << ans[i]+1 << ' ';
    }
    
    K-ON!!
  • 相关阅读:
    Python 学习 —— 进阶篇(装饰器、类的特殊方法)
    Python 基础学习的几个小例子
    MyBatis——特殊传参问题小结
    为什么要有分布式事务 分布式事务解决的什么问题 一次解答
    2pc事务和3pc事务区别详解
    SPEL语法
    分布式事务框架 TX-LCN 使用
    分布式事务解决方案
    excel 使用总结
    nginx 常用配置
  • 原文地址:https://www.cnblogs.com/pophirasawa/p/13694497.html
Copyright © 2011-2022 走看看