队列与单调队列算法主要内容
一、基本思路
1、队列模拟
-
队列数组 q【】,队头指针 hh = 0,队尾指针 tt = -1
-
主要特点:队尾插入元素,队头弹出元素
-
- 插入元素 q【++ tt】 = x
-
- 弹出 hh++
-
- 判断队列是否为空:
-
- hh <= tt ——非空
-
- hh > tt ——空
-
- 取出队头元素:q【hh】
- 取出队头元素:q【hh】
2、单调队列——滑动窗口经典问题
3、单调队列求解
-
原理:队尾数值 >= 遍历数 ,队尾被弹出,实现的是单调递增,否则递减
- 注意:
-
- 得到单调队列之后,只需要取队头即可,q【hh】
-
- 滑动窗口要考虑窗口大小问题,所以说队列中存的是 索引 index
- 滑动窗口要考虑窗口大小问题,所以说队列中存的是 索引 index
-
步骤:
-
- 判断队头是否已经滑出窗口 ——> 用下表判断
-
- 1)判空 hh <= tt ——> 非空
-
- 2)判断队列 检测区间是否已经大于k——>若 i-k+1>q【hh】,则 hh ++
-
- 若队尾所存索引对应值 >= 现在要插入的值,则队尾不可用,删掉即可
-
- 由单调性可知,队尾大则不可用
-
- 将当前的索引插入队列中
-
- 仅仅当序列指针大于滑动窗口的时候,才可以输出队头,一次输出一个,注意索引从0开始
二、Java、C语言模板实现
//Java 实现
// 模拟队列
import java.util.Scanner;
// 模拟队列
public class Main {
static int N = 100010;
static int[] q = new int[N];
static int hh = 0;
static int tt = -1;
public static void push(int x){ // 队尾插入
q[++tt] = x;
}
public static void pop(){ // 向队头弹出一个数
hh++;
}
public static void empty(){ // 判断是否为空
if (hh > tt){
System.out.println("YES");
}else {
System.out.println("NO");
}
}
public static void query(){ // 查询队头元素
System.out.println(q[hh]);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int M = scanner.nextInt();
int x;
for (int i = 0; i < M; i++) {
String judge = scanner.next();
switch (judge){
case "push":
x = scanner.nextInt();
push(x);
break;
case "pop":
pop();
break;
case "empty":
empty();
break;
case "query":
query();
break;
}
}
}
}
// 滑动窗口
import java.util.*;
import java.io.*;
public class Main {
// 单调队列——滑动窗口
static int N = 1000010;
static int[] q = new int[N];
static int[] a = new int[N];
static int hh = 0;
static int tt = -1;
public static void main(String[] args) throws IOException {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); // 快读
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out)); // 快出
st.nextToken();
int n = (int) st.nval;
st.nextToken();
int k = (int) st.nval;
for (int i = 0; i < n; i++) {
st.nextToken();
a[i] = (int) st.nval;
}
for (int i = 0; i < n; i++) { // 判断最小值
while (hh <= tt && i - k + 1 > q[hh]){
hh++;
}
while (hh <= tt && a[q[tt]] >= a[i]){ // 这里形成的是单调递增数列,因为新增加的优先级更高,并且只要大于它的都要被干掉
tt--;
}
q[++tt] = i;
if (i >= k - 1){
pw.print(a[q[hh]] + " ");
}
}
pw.println();
hh = 0;
tt = -1;
for (int i = 0; i < n; i++) {
while (hh <= tt && i - k + 1 > q[hh]){
hh++;
}
while (hh <= tt && a[q[tt]] <= a[i]){ // 这里形成的是单调递减数列,因为新增加的优先级更高,并且只要小于它的都要被干掉
tt--;
}
q[++tt] = i;
if (i >= k - 1){
pw.print(a[q[hh]] + " ");
}
}
pw.flush();//关闭输出流
}
}
```c
// C语言实现,此处是yxc实现
/// 模拟队列
1. 普通队列:
// hh 表示队头,tt表示队尾
int q[N], hh = 0, tt = -1;
// 向队尾插入一个数
q[ ++ tt] = x;
// 从队头弹出一个数
hh ++ ;
// 队头的值
q[hh];
// 判断队列是否为空,如果 hh <= tt,则表示不为空
if (hh <= tt)
{
}
2. 循环队列
// hh 表示队头,tt表示队尾的后一个位置
int q[N], hh = 0, tt = 0;
// 向队尾插入一个数
q[tt ++ ] = x;
if (tt == N) tt = 0;
// 从队头弹出一个数
hh ++ ;
if (hh == N) hh = 0;
// 队头的值
q[hh];
// 判断队列是否为空,如果hh != tt,则表示不为空
if (hh != tt)
{
}
/// 单调队列模拟——滑动窗口
常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
while (hh <= tt && check_out(q[hh])) hh ++ ; // 判断队头是否滑出窗口
while (hh <= tt && check(q[tt], i)) tt -- ; // 判断是选取滑动窗口中的哪一个值
q[ ++ tt] = i;
}
三、注意事项
import java.io.*;
public class test {
public static void main(String args[]) throws IOException{ // 注意IO异常抛出
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
String str = st.sval;//读取String类型数据
st.nextToken();
double num1 = st.nval;//读取double类型数据
st.nextToken();
int num2 = (int)st.nval;//读取int类型数据
st.nextToken();
long num3 = (long)st.nval;//读取long类型数据
}
}
// 非原创
-
- 快出
import java.io.*;
public class test {
public static void main(String args[]){
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
pw.print(); //不换行输出
pw.println(); //换行输出
pw.printf(); //格式化输出
pw.flush(); //关闭输出流————此处一定要记住
}
}
// 非原创