单调队列

概念

首先,单调队列,顾名思义,是一个队列呈递增或递减的特性,一般在算法中用来动态的保存一组数中的最大或最小值

简述

1.队列组成:一个整形的数组,begin,end来存放队列开始和结束的索引
2.队列大小:由于单调队列其实是一数组,不是queue,所以我们是通过他的begin和end的动态移动来模拟队列的,所以数组的大小尽量取大一些,给索引的移动提供足够的空间,例如经典的滑动窗口的例题,数组的大小应该和存储所有数据的数组大小一样
3.队列原理:就是通过begin和end的移动,来使在begin和end中间的数(包括两端)都满足单调特性,达到模拟队列的效果
4.队列中存放的是索引

例题

给定一组数据,n为数据个数,函数f(i)表示从第i个数开始k个连续数中的最小值,求i从0到n-k+1的所有f的值

代码

#include <stdio.h>
#include <sstream>
#include <iostream>
using namespace std;
int main(void) {
	int a[10] = {5,2,6,8,10,7,4,8,10,52};
	int k = 4;
	int q[10];//q来存索引
	int begin = 1;
	int end = 1;
	q[begin] = 0;
	for (int i = 1; i < 10; i++) {
		if (q[begin]+3 < i)//如果单调队列的开头已经出了窗口,队列就往后移一个
			begin++;
		if (a[q[end]] <= a[i])//如果插入的数比数组里面最大的数大,就添加该数索引
			q[++end] = i;
		else {
			while (end >= begin&&a[q[end]] > a[i])
				end--;
				q[++end] = i;
			}
		if (i >= 3)
		{
			for (int i = begin; i <= end; i++) {
				cout << a[q[i]] << " ";
			}
			cout << "\n";
		}
	}
	system("pause");
}

解析

  1. 首先这个一个滑动窗口的题目,
    在这里插入图片描述
  2. 代码中用数组q来存放单调队列,首先我们明确一点,我们要保证在begin和end中使一个单调队列,那么,在插入的时候应该保持这个特性
  3. 代码开始,我们设begin为1,end为1,可能有人很奇怪为什么不为0?为0也行,我们先往下看,等下就知道原因了。
  4. 由于后续的插入数据要有一个比较的基础,所以我们先把q[begin]=0。
  5. 接下来开始插入,我们先明确一点,窗口是在移动的,所以不同情况代码不同,一般的思路是先把刚开始的窗口的队列初始化好,然后再移动,分两步,但是这里代码中没有分,只有一个循环,原因是因为,窗口移不移动,数据一直再插入,或者说尝试插入,所以插入的代码会持续整个过程,只要再循环中加一个判断语句来对付移动时候的数据的出列就行啦。
  6. 代码进入循环,首先有一个判断
if (q[begin]+3 < i)//如果单调队列的开头已经出了窗口,队列就往后移一个
			begin++;

这里窗口大小为4,那么如果i已经超过了begin的对于的索引,也就意味这begin出了窗口,这个数无效了,begin就往后移动一位
在这里插入图片描述在这里插入图片描述
6.插入判断,如果要插入的数比末尾的数要大,那就排在end后面就行,可以保持单调性,如果比end对对应的数小,那么就一直找到比该数小的数为止,排在那个数的后面即可,其他数就失效了,begin和end之间还是能保持单调性,不懂的话把过程画一遍就知道啦,这里begin为1比为0要好,因为这里要有end–,这里begin为0的话,第二个数2取插入的时候就有越界的风险,当然,你如果一定要为0也可以加一个判断也行。
这里有一个写的不错的单调队列的应用推荐
单调队列应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值