概念
首先,单调队列,顾名思义,是一个队列呈递增或递减的特性,一般在算法中用来动态的保存一组数中的最大或最小值
简述
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");
}
解析
- 首先这个一个滑动窗口的题目,
- 代码中用数组q来存放单调队列,首先我们明确一点,我们要保证在begin和end中使一个单调队列,那么,在插入的时候应该保持这个特性
- 代码开始,我们设begin为1,end为1,可能有人很奇怪为什么不为0?为0也行,我们先往下看,等下就知道原因了。
- 由于后续的插入数据要有一个比较的基础,所以我们先把q[begin]=0。
- 接下来开始插入,我们先明确一点,窗口是在移动的,所以不同情况代码不同,一般的思路是先把刚开始的窗口的队列初始化好,然后再移动,分两步,但是这里代码中没有分,只有一个循环,原因是因为,窗口移不移动,数据一直再插入,或者说尝试插入,所以插入的代码会持续整个过程,只要再循环中加一个判断语句来对付移动时候的数据的出列就行啦。
- 代码进入循环,首先有一个判断
if (q[begin]+3 < i)//如果单调队列的开头已经出了窗口,队列就往后移一个
begin++;
这里窗口大小为4,那么如果i已经超过了begin的对于的索引,也就意味这begin出了窗口,这个数无效了,begin就往后移动一位
6.插入判断,如果要插入的数比末尾的数要大,那就排在end后面就行,可以保持单调性,如果比end对对应的数小,那么就一直找到比该数小的数为止,排在那个数的后面即可,其他数就失效了,begin和end之间还是能保持单调性,不懂的话把过程画一遍就知道啦,这里begin为1比为0要好,因为这里要有end–,这里begin为0的话,第二个数2取插入的时候就有越界的风险,当然,你如果一定要为0也可以加一个判断也行。
这里有一个写的不错的单调队列的应用推荐
单调队列应用