zoukankan      html  css  js  c++  java
  • 滑动窗口 单调队列

    题目描述
    给一个长度为N的数组,一个长为K的滑动窗体从最左端移至最右端,你只能看到窗口中的K个数,每次窗体向右移动一位,如下图:
    在这里插入图片描述
    你的任务是找出窗体在各个位置时的最大值和最小值。
    输入描述:
    第1行:两个整数N和K;
    第2行:N个整数,表示数组的N个元素;
    输出描述:
    第一行为滑动窗口从左向右移动到每个位置时的最小值,每个数之间用一个空格分开;
    第二行为滑动窗口从左向右移动到每个位置时的最大值,每个数之间用一个空格分开。

    示例:
    输入
    8 3
    1 3 -1 -3 5 3 6 7
    输出
    -1 -3 -3 -3 3 3
    3 3 5 5 6 7
    备注:
    对于20%的数据,K≤N≤1000;
    对于50%的数据,K≤N≤10^5;
    对于100 %100%的数据,K≤N≤10^6。

    最开始想的是挪动窗口时更新加入的值和删除的值,发现不可行,如果删除的值恰好是最值而且窗口内有多个向等的值就不行了,然后想干脆线段树吧,nlogn的时间复杂度还凑合,然后不幸的是超内存。。。看题解发现是单调队列,

    假设求最值时,我们维护一个单调递增的队列,每次从队列尾部将当前值插入进去,而且我们要找窗口的最小值,插入位置后边的值都大于当前值,而且队列里面的值都在当前值右边,不会比当前值晚出队,所以这些值已经没用了,可以直接删去,队列内是单调递增的所以队头位置就是当前的最小值,需要输出的时候输出队头就可以了,但是还有一个问题,窗口长度一定为k,窗口之前的值要删掉,只要在输出之前判断队头元素是否在窗口内就可以,所以队列里面就不能存值了,而要存数组的下标,有了下标就可以找到具体的值还可以判断是否在窗口内,一举两得。
    找最大值与上面是一样的,每个元素最多入队出队各一次,复杂度O(n)

    #include<iostream>
    using namespace std;
    typedef long long ll;
    const int N = 1e6+10;
    int n,k,a[N],dq[N],l,r;
    int main()
    {
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	cout.tie(0);
    	cin>>n>>k;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<=n;i++)
    	{
    		while(r>=l&&a[i]<a[dq[r]]) r--;//队列尾部大于ai的都可以删掉了,找到ai应该插入的位置
    		dq[++r]=i;//下标插入队列
    		while(dq[l]<=i-k) l++;//删除队首不在窗口内的元素,一个if应该就可以了,每次移动只删除一个元素,上上个删除的在上次已经删除了
    		if(i>=k)//构成了完整的窗口可以输出了
    			cout<<a[dq[l]]<<' ';
    	}
    	cout<<endl;
    	l=r=0;
    	for(int i=1;i<=n;i++)//完全同上
    	{
    		while(l<=r&&a[i]>=a[dq[r]]) r--;
    		dq[++r]=i;
    		while(dq[l]<=i-k) l++;
    		if(i>=k) cout<<a[dq[l]]<<' ';
    	}
    	return 0;
    }
    
  • 相关阅读:
    vue 给嵌套的iframe子页面传数据 postMessage
    左边宽度固定,右边宽度自适应的三种写法
    全局变量声明的规范化
    利用__index和__newindex实现默认值表、监控表、只读表
    Metatable和Metamethod
    Lua中的协同程序 coroutine
    Lua中的require
    Lua基础
    D3D的绘制
    效率相关笔记
  • 原文地址:https://www.cnblogs.com/neflibata/p/12871768.html
Copyright © 2011-2022 走看看