Week5:滑动窗口——单调队列

题目内容
有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少. 例如:
数列是 [1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
在这里插入图片描述
输入格式
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示待分析的数列。

输出格式
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。

输入样例

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

输出样例

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

题目分析
显然,如果对每个区间暴力求解,一旦k大起来,那时间复杂度直接上天了都。
对于此题,我们可以维护一个单调队列,并且是采用的deque双端队列,这样子我们就可以每一次只维护某个区间内的单调性了。
维护这个双端队列,把此队列的左端点的位置看作是起点,那么对于每次这个队列(即窗口)移动的时候,右端维护单调性,左端把边界以外的点丢出去,就能够只扫描一遍就求出每个区间下的最小值了。

最大值同理。

#include <iostream>
#include <vector>
#include <string>
#include <deque>
using namespace std;

int n, k;

struct Point
{
	int num;
	int index;
	Point(int n,int i):num(n),index(i){}
};

deque<Point>minwindow;
deque<Point>maxwindow;
vector<int>minans;
vector<int>maxans;

void insert(Point p)
{
	while (!minwindow.empty() && minwindow.back().num > p.num)  minwindow.pop_back();
	minwindow.push_back(p);

	while (!maxwindow.empty() && maxwindow.back().num < p.num)  maxwindow.pop_back();
	maxwindow.push_back(p);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin >> n >> k;

	int temp;
	//初始化
	for (int i = 0; i < k - 1; i++)
	{
		cin >> temp;
		Point t(temp, i);

		insert(t);
	}

	for (int i = k - 1; i < n; i++)
	{
		int index = i - k + 1;//窗口最左边的位置
		cin >> temp;
		Point t(temp, i);

		insert(t);
		//弹出窗口最左边之前的数
		while (minwindow.front().index < index) minwindow.pop_front();
		while (maxwindow.front().index < index) maxwindow.pop_front();

		minans.push_back(minwindow.front().num);
		maxans.push_back(maxwindow.front().num);
	}

	for (int i = 0; i < minans.size(); i++)
	{
		cout << minans[i] << ' ';
	}

	cout << endl;

	for (int i = 0; i < maxans.size(); i++)
	{
		cout << maxans[i] << ' ';
	}
}
发布了21 篇原创文章 · 获赞 3 · 访问量 406

猜你喜欢

转载自blog.csdn.net/qq_44506233/article/details/105105845
今日推荐