单调队列 + 例题:洛谷P2032 扫描 题解

因为博主本人水平有限,文章中难免会出现错误。您如果发现文章的错误敬请批评指正,感谢您的阅读^_^

一、什么是 单调队列

        单调队列 是一种具有单调性的结构,它可以用来求最大值最小值等操作,它是一种特殊的双端队列,它保持队列中元素的单调性。单调队列可以在O(1)的时间复杂度内求得队列中的最大值或最小值。在单调队列中,队列中的元素按照一定的顺序排列,并且队列中的元素满足某种单调性。常见的单调队列有单调递增队列单调递减队列。在单调递增队列中,队列中的元素按照递增的顺序排列;在单调递减队列中,队列中的元素按照递减的顺序排列。单调队列常用于求解滑动窗口问题,可以高效地获取滑动窗口内的最大值或最小值

二、单调队列的基本思想

        单调队列 的基本思想是:维护一个单调递增(或递减)的队列,队列中的元素按照从大到小(或从小到大)的顺序排列。每当窗口滑动时,如果新加入的元素大于队尾元素,则将队尾元素出队,直到队列为空或新加入的元素小于等于队尾元素为止。这样,队列的队首元素就是当前窗口的最大值。 

        这段话有点长,可以搭配后面的内容理解。

三、示意图 

以下是一个示意图,展示了单调队列的基本思想和操作过程:

初始状态:
队列:(5)

操作1:
入队元素3
此时3比5小,直接入队
队列:(5,3)

操作2:
入队元素2
此时2比3小,直接入队
队列:(5,3,2)

操作3:
入队元素4
此时进行判断,发现4比2大,2出队
再次进行判断,发现4比3大,3出队
再次进行判断,发现4比5小,4入队
队列:(5,4)

操作4:
入队元素2
队列:(5,4,2 )

操作5:
这里我们尝试出队一个元素
队列:(4,2)
我们会发现最大值依然是最左边的数
当前最大元素:4

        以上示意图展示了一个单调递减队列的操作过程,队列中始终保持着元素的递减顺序。在每次入队时,如果新元素比队尾元素大,则将队尾元素出队,直到队列为空或新元素比队尾元素小为止。这样就实现了维护一个单调递减序列的队列。

四、代码实现

下面给出单调队列在滑动窗口问题中的C++实现示例:

#include <iostream>
#include <deque>
#include <vector>

using namespace std;

void maxSlidingWindow(int nums[], int k) {
	deque<int> dq;
	for (int i = 0; i < 8; i++) {
		// 如果队列不为空且队尾元素小于当前元素,将队尾元素出队
		while (!dq.empty() && nums[i] >= nums[dq.back()]) {
			dq.pop_back();
		}
		
		// 将当前元素入队
		dq.push_back(i);
		
		// 判断队首元素是否在滑动窗口内,如果不在则出队
		while (dq.front() <= i - k) {
			dq.pop_front();
		}
		
		// 判断滑动窗口是否形成,如果形成则将队首元素(最大值)加入结果列表
		if (i >= k - 1) {
			cout << nums[dq.front()] << " ";
		}
	}
}

int main() {
	int nums [] = {1, 3, -1, -3, 5, 3, 6, 7};
	int k = 3;
	
	maxSlidingWindow(nums, k);
	
	return 0;
}
输出:
3 3 5 5 6 7


以上是一个完整的C++程序,实现了使用单调队列解决滑动窗口中的最大值问题。
示例中的输入数组 `nums` 为 {1, 3, -1, -3, 5, 3, 6, 7},窗口大小 `k` 为 3,程序的输出为 `3 3 5 5 6 7`,即滑动窗口中的最大值。

        在示例代码中,使用`deque`作为单调队列,通过维护队列的单调递减性质来求解最大值。程序首先遍历输入数组,将元素通过一定的逻辑加入到队列中,并保持队列的单调性。然后,判断队首元素是否在滑动窗口内,如果不在,则从队首弹出元素。最后,判断滑动窗口是否形成,如果形成则将队首元素(最大值)加入结果列表。

五、例题讲解

        洛谷P2032 扫描

   (一)题面

        1.题目描述

有一个 1×n 的矩阵,有 n 个整数。

现在给你一个可以盖住连续 k 个数的木板。

一开始木板盖住了矩阵的第 1∼k 个数,每次将木板向右移动一个单位,直到右端与第 n 个数重合。

每次移动前输出被覆盖住的数字中最大的数是多少。

        2.输入格式

第一行两个整数 n,k,表示共有 n 个数,木板可以盖住 k 个数。

第二行 n 个整数,表示矩阵中的元素。

        3.输入格式 

共 n−k+1 行,每行一个整数。

第 i 行表示第 i∼i+k−1 个数中最大值是多少。

   (二)题目解析 

         这道题我们可以创建一个单调队列,具体操作看一下代码和注释

   (三)具体代码

         老规矩,上代码,走你q(≧▽≦q)

//今天你数组调大小了吗

#include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <stack>
#include <vector>
#include <set>
#include <cstdio>
#include <cmath>
#include <map>
#include <cstdlib>

using namespace std;

struct point{//建立结构体 
	int x;   //存储元素
	int t;   //元素的下标
};
int n,k;
list<point> l;  //这里我们用链表当单调队列

int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		int a;
		cin>>a;  //临时存放元素
		while(!l.empty()&&l.back().x<=a){ //清除比它小的
			l.pop_back();
		}
		l.push_back({a,i}); //进入单调队列,此时在它左边没有比它小的 
							//保证了队列的单调性
		if(l.front().t<i-k+1){
			l.pop_front();  //将木板盖不住的元素清除
		}
		if(i>=k){   //此时可以开始输出了
			cout<<l.front().x<<endl;
		}
	}
	
	return 0;
}

  • 15
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值