十三、数据结构——队列与单调队列

一、基本思路

1、队列模拟

  • 队列数组 q【】,队头指针 hh = 0,队尾指针 tt = -1
  • 主要特点:队尾插入元素,队头弹出元素
    1. 插入元素 q【++ tt】 = x
    1. 弹出 hh++
    1. 判断队列是否为空:
    • hh <= tt ——非空
    • hh > tt ——空
    1. 取出队头元素:q【hh】
      在这里插入图片描述

2、单调队列——滑动窗口经典问题

  • 求解滑动窗口中的最大值/最小值问题
    • 其中方框代表的是滑动框
      在这里插入图片描述
  • 暴力做法
    • 维护一个队列进行遍历(看那些没有用,也就是不满足条件),时间复杂度O(nk)

3、单调队列求解

  • 原理:队尾数值 >= 遍历数 ,队尾被弹出,实现的是单调递增,否则递减
  • 注意:
    • 得到单调队列之后,只需要取队头即可,q【hh】
    • 滑动窗口要考虑窗口大小问题,所以说队列中存的是 索引 index
      在这里插入图片描述
  • 步骤:
    1. 判断队头是否已经滑出窗口 ——> 用下表判断
    • 1)判空 hh <= tt ——> 非空
    • 2)判断队列 检测区间是否已经大于k——>若 i-k+1>q【hh】,则 hh ++
    1. 队尾所存索引对应值 >= 现在要插入的值,则队尾不可用,删掉即可
    • 由单调性可知,队尾大则不可用
    1. 将当前的索引插入队列中
    • 仅仅当序列指针大于滑动窗口的时候,才可以输出队头,一次输出一个,注意索引从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();			//关闭输出流————此处一定要记住
	}
}

// 非原创
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牙否

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值