单调队列 模板

       何为单调队列?,单调队列是指一个队列内部的元素具有严格单调性的数据结构,分为单调递增队列和单调递减队列,单调队列需要满足两个性质:
       1>单调队列必须满足从队首至队尾的严格单调性。
       2>排在队列前面的元素要比排在队列后面的元素先进队。
       单调队列问题也可以被称为"滑动窗口"为问题,它是维持一个单调窗口,元素进队列时,需要与队尾元素进行比较,在维持的窗口单调递增的情况下,如果该元素大于队尾元素,可以直接将该元素扔进队列,否则不断将队尾元素出队(–tail),直至满足该元素大于队尾元素。
       题目描述
  有一个长度为 n ( n < = 1 e 6 ) n(n <= 1e6) n(n<=1e6),假设有一个长度为 k k k的滑动窗口从数组的最左边一直滑倒数组的最右边。在窗口进行滑动时,我们只能看到窗口内的 k k k个数组,且窗口每次只能向右滑动一个元素,假设数组为[1 3 -1 -3 5 3 6 7],判断窗口每个位置的最小值和最大值。
  [Sample input]

8 3
1 3 -1 -3 5 3 6 7

  [Sample output]

-1 -3 -3 -3 3 3
3 3 5 5 6 7

  倘若以最大值为例,此时该队列为一个单调递减队列,元素从左至右依次入队,入队必然从队列尾部开始入队,此时与队列尾部元素进行比较,如果比队列元素小就直接扔进队列,否则删除当前队尾元素继续比较,直到找到一个比队尾元素小或为空的情况,在扔进队列。如果队列的大小超过窗口值则删除队首元素,直至队列大小小于窗口值为止,然后将当前元素插入队尾,每次取队首元素(最大值)即可
  代码如下:

#include 
#include 
using namespace std;
const int N = 1e6 + 6;//1000006
int arr[N], n, k;
int pos[N], que[N], MAX[N], MIN[N];

//8 3(窗口大小为 3)
//1 3 -1 -3 5 3 6 7

void get_Min()
{
	int head = 1, tail = 0;
//	满足队列 的单调性为单调递增 
	for(int i = 1; i <= n; ++i)
	{
		while(head <= tail && que[tail] > arr[i]) --tail;//队尾元素 > 当前元素, tail前移至 队尾元素 <= 当前元素//插入 
		que[++tail] = arr[i];
		pos[tail]   = i;
		if(i < k) continue;//窗口大小未至 k 
		while(pos[head] < i - k + 1) ++head;//删队头, 因为在滑动过程中, 队头元素会被舍弃 
		MIN[i - k + 1] = que[head]; 
	}
	for(int i = 1; i <= n - k + 1; ++i) printf("%d ", MIN[i]);
	printf("\n");
}

void get_Max()
{
	int head = 1, tail = 0;//队首 --> 1, 队尾 --> 0
//	满足队列 的单调性为单调递减 
	for(int i = 1; i <= n; ++i)
	{
		while(head <= tail && que[tail] <= arr[i]) --tail;//队尾元素 <= 当前元素, tail 前移直至 队尾元素 > 当前元素//插入 
		que[++tail] = arr[i];
		pos[tail] = i;
		if(i < k) continue;//窗口大小未至 k
		while(pos[head] < i - k + 1) head++;              //滑动窗口过大即超过了 k, 删队头 

		MAX[i - k + 1] = que[head];                       //取队头(保证在当前滑动窗口内最大)元素 
	}
	for(int i = 1; i <= n - k + 1; ++i) printf("%d ", MAX[i]);	
	printf("\n");	
} 
 
int main()
{
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; ++i) scanf("%d", &arr[i]);
	get_Min(); 
	get_Max();
	return 0; 
}

你可能感兴趣的:(动态规划)