队列 - Queue

2023-11-13

1、队列概述

1、队列,又称为伫列(Queue),计算机科学中的一种抽象资料型别,是先进先出(FIFO, First-In-First-Out)的线性表。队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。
2、队列中没有元素时,称为空队列。队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队(Enqueue),从队列中删除一个队列元素称为出队(Dequeue)。
3、和栈一样,队列是一种操作受限制的线性表。队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。
4、队列在具体应用中通常用链表或者数组来实现。

用大O符号表示队列的时间复杂度:

算法 空间 搜索 插入 删除
平均 O(n) O(n) O(1) O(1)
最差 O(n) O(n) O(1) O(1)

2、用数组模拟队列

2.1 思路分析

(1)队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize表示该队列的最大容量。
(2)因为队列的输出、输入是分别从前、后端来处理,因此需要两个变量front、rear分别记录队列前、后端的下标。front会随着数据输出而改变,rear则是随着数据输入而改变,如图所示:
front是队列最前一个元素[不含]
rear是队列最后一个元素[含]

数组模拟队列

图1 数组模拟队列

(3)当我们将数据存入队列时称为”addQueue”操作,它需要两个步骤实现:
① 将尾指针往后移,即rear + 1。当front = rear时,则队列为空。
② 若尾指针rear小于队列的最大下标减1,即rear < maxSize - 1(此处的maxSize - 1就是数组最大索引),则将数据存入rear所指的数组元素中,否则无法存入数据,即当rear = maxSize - 1时,队列已满。

2.2 代码实现

数组模拟队列:

package com.example.datastructure.queue;


/**
 * @author Administrator
 * @date 2020/12/30
 * <p>
 * 用数组模拟队列
 */
class ArrayQueue {
    /**
     * 表示数组的最大容量
     */
    private int maxSize;
    /**
     * 队列头
     */
    private int front;
    /**
     * 队列尾
     */
    private int rear;
    /**
     * 用于存放数据,模拟度列
     */
    private int[] array;

    public ArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        array = new int[maxSize];
        // 指向队列头的前一个位置
        front = -1;
        // 指向队列尾部的最后一个数据
        rear = -1;
    }

    /**
     * 判断队列是否已满
     */
    public boolean isFull() {
        return rear == maxSize - 1;
    }

    /**
     * 判断队列是否为空
     */
    public boolean isEmpty() {
        return rear == front;
    }

    /**
     * 添加数据到队列
     */
    public void addQueue(int data) {
        if (isFull()) {
            System.out.println("队列满,不能添加数据");
            return;
        }
        // 让rear后移
        rear++;
        array[rear] = data;
    }

    /**
     * 获取队列的数据,出队列
     */
    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,不能取数据");
        }
        // front后移
        front++;
        return array[front];
    }

    /**
     * 显示队列的所有数据
     */
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列空的,没有数据");
            return;
        }
        for (int i = 0; i < array.length; i++) {
            System.out.printf("array[%d]=%d\n", i, array[i]);
        }
    }

    /**
     * 显示队列头的数据,但并不取出
     */
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空的,没有数据");
        }
        return array[front + 1];
    }

}

对数组模拟队列进行测试,测试代码如下:

package com.example.datastructure.queue;

import java.util.Scanner;

/**
 * @author Administrator
 * @date 2021/1/12
 * 测试-数组模拟队列
 */
class ArrayQueueDemo {
    public static void main(String[] args) {
        // 测试
        ArrayQueue queue = new ArrayQueue(3);
        // 接收用户的输入
        char key;
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        // 输出一个菜单
        while (loop) {
            System.out.println("s(show):显示队列");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列取出数据");
            System.out.println("h(head):查看队列头的数据");
            System.out.println("e(exit):退出程序");
            // 接收一个字符
            key = scanner.next().charAt(0);
            switch (key) {
                case 's':
                    queue.showQueue();
                    break;
                case 'a':
                    System.out.println("添加一个数据");
                    int value = scanner.nextInt();
                    queue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int data = queue.getQueue();
                        System.out.printf("取出的数据是%d\n", data);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int headValue = queue.headQueue();
                        System.out.printf("队列头的数据是%d\n", headValue);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出");
    }
}

测试效果:

2.3 问题分析与优化

问题:数组使用一次就不能用了,没有达到复用的效果(数组只能使用一次,数据出队列后无法继续使用,不能重新添加数据)。
优化:使用算法(取模:%),将其改造为环形队列。

3、数组模拟环形队列

对前面的数组模拟队列进行优化,将数组看做是一个环(通过取模%的方式实现),就可以解决数组无法复用的问题,从而充分利用数组。

3.1 分析说明

(1)front变量的含义做一个调整:front的初始值为0,front就指向队列的第一个元素,即arr[front]就是队列的第一个元素。
(2)rear变量的含义做一个调整:rear的初始值为0,rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间做为约定。即,尾索引的下一个为头索引时表示队列已满。
(3)当(rear + 1) % maxSize = front时,队列已满。
(4)对rear = front时,队列为空。
(5)经上述分析,队列中有效的数据个数 = (rear + maxSize - front) % maxSize。
经以上调整,我们就可以修改原来的数组模拟队列,得到一个新的环形队列。

经典公式:
对于一个固定大小的数组,任何位置都可以是队首,只要知道队列长度,就可以根据下面公式计算出队尾位置:
tailIndex = (headIndex + count − 1) mod capacity
其中capacity是数组长度,count是队列长度,headIndex和tailIndex分别是队首head和队尾tail索引。

3.2 代码实现环形队列

数组模拟环形队列:

package com.example.datastructure.queue;

/**
 * @author Administrator
 * @date 2020/12/30
 * <p>
 * 用数组模拟环形队列
 */
class CircleArrayQueue {
    /**
     * 表示数组的最大容量
     */
    private int maxSize;
    /**
     * front变量的含义做一个调整:
     * front的初始值为0,front就指向队列的第一个元素,即arr[front]就是队列的第一个元素。
     */
    private int front;
    /**
     * rear变量的含义做一个调整:
     * rear的初始值为0,rear指向队列的最后一个元素的后一个位置,因为希望空出一个空间做为约定。
     * 即,尾索引的下一个为头索引时表示队列已满。
     */
    private int rear;
    /**
     * 用于存放数据,模拟队列
     */
    private int[] array;

    public CircleArrayQueue(int maxSize) {
        this.maxSize = maxSize;
        array = new int[maxSize];
    }

    /**
     * 判断队列是否已满
     */
    public boolean isFull() {
        return (rear + 1) % maxSize == front;
    }

    /**
     * 判断队列是否为空
     */
    public boolean isEmpty() {
        return rear == front;
    }

    /**
     * 添加数据到队列
     */
    public void addQueue(int data) {
        if (isFull()) {
            System.out.println("队列满,不能加入更多~");
            return;
        }
        array[rear] = data;
        // 将rear后移,这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

    /**
     * 获取队列数据,出队列
     */
    public int getQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无数据出列~");
        }
        /*
        这里需要分析出front是指向队列的第一个元素.
        1、先把front对应的值保留到一个临时变量
        2、将front后移,考虑取模
        3、将临时变量返回
         */
        int temp = array[front];
        front = (front + 1) % maxSize;
        return temp;
    }

    /**
     * 显示队列全部数据
     */
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列空,没有显示内容~");
            return;
        }
        /*
        思路:从front开始遍历
         */
        for (int i = front; i < front + effectiveNum(); i++) {
            System.out.printf("array[%d]=%d\n", i % maxSize, array[i % maxSize]);
        }
    }

    /**
     * 求出当前队列有效数据个数
     */
    public int effectiveNum() {
        return (rear + maxSize - front) % maxSize;
    }

    /**
     * 显示队列头数据,但不出列
     */
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无头数据~");
        }
        return array[front];
    }
    
    /**
     * 获取队尾数据,不出列
     */
    public int rearQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无尾数据~");
        }
        return array[(rear - 1) & (array.length - 1)];
    }
}

测试环形队列:

package com.example.datastructure.queue;

import java.util.Scanner;

/**
 * @author Administrator
 * @date 2021/1/20
 */
class CircleArrayQueueDemo {
    public static void main(String[] args) {
        System.out.println("测试数组模拟环形队列:");

        //说明:设置为4,则队列的有效数据个数为3
        CircleArrayQueue circleQueue = new CircleArrayQueue(4);
        char key;
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;

        while (loop) {
            System.out.println("s(show): 显示队列");
            System.out.println("e(exit): 退出程序");
            System.out.println("a(add): 添加数据到队列");
            System.out.println("g(get): 从队列取出数据");
            System.out.println("h(head): 查看队列头的数据");
            System.out.println("r(rear): 查看队尾的数据");
            // 接收一个字符
            key = scanner.next().charAt(0);
            switch (key) {
                case 's':
                    circleQueue.showQueue();
                    break;
                case 'a':
                    System.out.println("添加数据:");
                    int value = scanner.nextInt();
                    circleQueue.addQueue(value);
                    break;
                case 'g':
                    try {
                        int res = circleQueue.getQueue();
                        System.out.printf("取出的数据是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int res = circleQueue.headQueue();
                        System.out.printf("队列的头数据是%d\n", res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'r':
                    try {
                        int r = circleQueue.rearQueue();
                        System.out.printf("队尾的数据是%d\n", r);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");
    }

}

测试效果:
环形数组队列
说明:
本文整理自韩顺平老师的课程,强烈推荐韩老师的课。

微信公众号: TechU
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

队列 - Queue 的相关文章

随机推荐

  • 哪一类功率放大电路效率最高_让我们来复习一下功率放大电路与集成运算放大电路...

    功率放大电路 一 功率放大电路的三种工作状态 1 甲类工作状态 导通角为360度 ICQ大 管耗大 效率低 2 乙类工作状态 ICQ 0 导通角为180度 效率高 失真大 3 甲乙类工作状态 导通角为180o 360o 效率较高 失真较大
  • springboot整合Thymeleaf

    springboot的简化配置 在springboot中 视图解析器等注册都不需要我们做了 因为springboot都默认帮我们做了 只要我们将Thymeleaf maven依赖添加到项目的pom文件下 就启用了SpringBoot的自动配
  • go语言的Hash算法

    Hash project main go package main import C import crypto md5 crypto sha1 crypto sha256 crypto sha512 io ioutil unsafe fm
  • Mac 系统升级后 idea 提示Cannot run Git Couldn‘t install Git

    终端输入 git hepl 报错 xcrun error invalid active developer path Library Developer CommandLineTools missing xcrun at Library D
  • 函数的认识

    文章目录 函数是什么 库函数 自定义函数 函数参数 函数调用 函数的嵌套调用和链式访问 函数的声明和定义 函数递归 一 函数是什么 维基百科中对函数的定义 子程序 在计算机科学中 子程序 英语 Subroutine procedure fu
  • 至简原生与webview交互逻辑演示

    var callbackstack var callbackId 0 function getToken callbackId return new Promise rs rj gt window android postToNative
  • Linux系统上安装Vmware Workstation

    系统平台 RHEL6 1 X86 32bit 软件版本 VMware Workstation Full 8 0 0 471780 i386 bundle 说明 适用于其他平台 其他VMware Workstation版本 安装完成后要运行虚
  • 解决使用VS2015新建QT界面之后cpp文件提示“不允许使用不完整的类型”问题

    解决使用VS2015新建QT界面之后cpp文件提示 不允许使用不完整的类型 问题 问题描述 分析解决过程 总结 问题描述 分析解决过程 检查了半天跟另外一个文件的结构是一模一样的 只是类名不一样 网上找了很多方式都不行 真是让我脑热 后面我
  • 微信提示在客户端提交验证_微信提示非常用设备登陆需输入短信验证码的解决方法...

    已绑定邮箱 1 已设置独立密码 请您直接通过微信号 独立密码登录即可 2 未设置独立密码 可通过以下两种方式设置独立密码后登录 1 请您可以在登录界面输入微信号 点击 忘记密码 通过手机号验证码或邮箱重设密码 2 通过电脑登录weixin
  • C语言:运算表达式

    目录 一 赋值运算符与赋值表达式 二 逗号运算符和逗号运算符 三 关系运算符和关系表达式 四 逻辑运算符和逻辑表达式 五 条件运算符和条件表达式 六 scanf错误解决 七 总结 一 赋值运算符与赋值表达式 变量 表达式 特点 自右向左 a
  • rsync备份

    Rsync的特点和优点 可以镜像保存整个目录树和文件系统 可以很容易做到保持原来文件的权限 时间 软硬链接等等 无须特殊权限即可安装 快速 第一次同步时 rsync 会复制全部内容 但在下一次只传输修改过的文件 压缩传输 rsync 在传输
  • xss检测工具XSStrike

    一 下载安装 下载地址 https github com s0md3v XSStrike最新版支持python3windows linux系统都可以运行完成下载之后 进入XSStrike目录 cd XSStrike接下来使用如下命令安装依赖
  • Quartus II建立新工程流程,Quartus如何建立工程?

    在用Quartus Quartus Prime 18 0 Standard Edition开发一个项目时 首先要建立一个工程文件 这个工程文件包含了项目设计过程中生成的所有文件 创建的步骤大致如下 3 1 首先双击Quartus Quart
  • RISC-V IDE MounRiver Studio相较Eclipse GNU的区别与改进

    RISC V单片机集成开发环境 IDE MounRiver Studio相较Eclipse GNU的区别与改进 一 界面与功能区别 1 欢迎页 MounRiver Studio www mounriver com 左侧为工程操作及帮助文档快
  • 机器学习(二)分类器及回归拟合

    在机器学习中 分类器作用是在标记好类别的训练数据基础上判断一个新的观察样本所属的类别 分类器依据学习的方式可以分为非监督学习和监督学习 非监督学习顾名思义指的是给予分类器学习的样本但没有相对应类别标签 主要是寻找未标记数据中的隐藏结构 监督
  • 互补品的需求曲线图_需求曲线:需求曲线的移动

    我们已经知道市场需求量是社会中各个家庭或企业的需求总和 那么需求曲线的变动是什么因素引起的呢 这将是我们本次讨论的重点问题 我们在讨论市场需求需求曲线时假设的前提 其他条件都保持不变 但是随着时间的推移 该曲线不一定是稳定不变的 如果某种因
  • js双层循环拿到二层循环的index值

    情景描述 多个房间 每个房间的人数不尽相同 后端获取的数据格式是根据房间走的 如 data roomNo 201 guestList name 张三 name 李四 roomNo 202 guestList name 张三三 name 李四
  • 销售系统服务器,勤哲Excel服务器-销售管理系统(9页)-原创力文档

    勤哲 Excel 服务器 销售管理系统 一 系统框架 整个系统分为五部分 基础数据 销售管理 仓库管理 费用管理 经营分析 其中仓 库管理的详细系统可以参见 库存管理系统 本系统不做详细说明 二 各模块功能说明 一 基础数据 1 仓库信息
  • 抖音评论获取与回复源码项目

    这个项目分享的如何基于抖音平台 开发的java源码 API覆盖率超过95 只需要简单的修改一下配置文件 就能轻松调用api 自动集成官方SDK 切换使用原生一样方便 多种选择 轻松适配 根据视频大小 自动切换视频分片上传 轻松避免异常 保证
  • 队列 - Queue

    1 队列概述 1 队列 又称为伫列 Queue 计算机科学中的一种抽象资料型别 是先进先出 FIFO First In First Out 的线性表 队列是一种特殊的线性表 特殊之处在于它只允许在表的前端 front 进行删除操作 在表的后