数据结构--环形队列的介绍与实现

2023-11-10

环形队列可以用数组实现,也可以使用循环链表实现.在使用数组实现循环队列的时候,需要理解清楚队列头和队列尾的判断空还是满的处理方法;

环形队列的优点:

  • 避免假溢出现象(由于入队和出队的操作,头尾指针只增加不减少,致使被删元素的空间永远无法重新利用,当队列继续存储元素时,出现尾指针已经到达了队列尾,而实际头指针前面并未满的情况),可以将队列空间充分重复利用
  • 首尾相连的FIFO的数据结构,采用数据的线性空间,数据组织简单,能快速知道队列是否满/空
  • 广泛用于网络数据收发,和不同程序间数据交换,均使用了环形队列

一、环形队列实现原理

  • 内存上并没有环形的结构,因此环形队列实际上是数组的线性空间来实现的。

  • 当数据到了尾部该如何处理呢?它将转回到原来位置进行处理,通过取模操作来实现

环形队列的几个判断条件

front:指向队列的第一个元素,初始值front=0

rear: 指向队列的最后一个元素的后一个位置(预留一个空间作为约定),初始值rear=0

maxSize: 数组的最大容量

  • 队空:front == rear

  • 队满:(rear+1)%maxSize == front

  • 队列中的有效数据个数:(rear+maxSize-front)% maxSize

在这里插入图片描述

其中判断队列满的思想的话,可以看下图,因为是环形的,起初front=rear=0,每当添加元素时,将rear++,但是其实预留了一个长度没有用,比如定义的队列数组长度为5时,但是实际上可以使用的地址就是0,1,2,3,此时rear=4, 4这个空间用来判断队满的条件(rear+1)%maxSize==front

在这里插入图片描述

二、代码实现

1.环形队列类(CircleQueue)

/**
 * 环形队列类
 * 构造器
 * 判断是否已满、判断是否空、查看队列数据、显示队列的有效数据个数、入队列、出队列
 */

class CircleQueue {
//    数组的最大容量
    private final int maxSize;
//    front指向队列的第一个元素,初始值为0
    private int front;
//    rear指向队列的最后一个元素的后一个位置,空出一个空间作为约定,初始值为0
    private int rear;
//    存放数据,模拟队列
    private final int[] arr;

    //    创建队列构造器
    public CircleQueue(int maxSize) {
        this.maxSize = maxSize;
        front = 0;
        rear = 0;
        arr = new int[maxSize];
    }

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

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

    //    查看队列数据,显示队列所有数据
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列为空,没有数据!");
            return;
        }
//        从front开始遍历,注意遍历的元素个数
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }

    }

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

    //    显示队列的头数据,注意不是取出数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列是空的,没有数据!");
        }
        return arr[front];
    }

    //    添加数据到队列
    public void addQueue(int n) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }
        arr[rear] = n;
        System.out.println(n + "成功添加到队列");
        //        将rear后移,这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

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

2.环形队列类测试类

/**
 * @author Manix 
 * 环形队列测试类
 */

public class CircleArrayQueueDemo {
    public static void main(String[] args) {
//        创建一个环形队列,maxSize设置说明,4,其队列的有效数据最大为3
        CircleQueue circleQueue = new CircleQueue(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),获取队列头数据");
//          接收输入的字符
            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 'e':
                    loop = false;
                    scanner.close();
                    break;
                default:
                    break;
            }
        }
        System.out.println("-----程序退出-----");
    }
}

3.程序运行结果

s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
s
队列为空,没有数据!
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
a
输入一个数:
10
10成功添加到队列
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
a
输入一个数:
20
20成功添加到队列
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
a
输入一个数:
60
60成功添加到队列
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
a20
输入一个数:
20
队列已满
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
s
arr[0]=10
arr[1]=20
arr[2]=60
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
a
输入一个数:
30
队列已满
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
s
arr[0]=10
arr[1]=20
arr[2]=60
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
g
取出的数据是10
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
g
取出的数据是20
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
s
arr[2]=60
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据
h
队列头的数据是60
s(show),显示队列数据
e(exit),退出队列
a(add),添加数据到队列
g(get),获取队列数据
h(head),获取队列头数据

4.完整代码

/**
 * @author Manix
 * 环形队列测试类
 */

public class CircleArrayQueueDemo {
    public static void main(String[] args) {
//        创建一个环形队列,maxSize设置说明,4,其队列的有效数据最大为3
        CircleQueue circleQueue = new CircleQueue(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),获取队列头数据");
//          接收输入的字符
            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 'e':
                    loop = false;
                    scanner.close();
                    break;
                default:
                    break;
            }
        }
        System.out.println("-----程序退出-----");
    }
}

/**
 * 环形队列类
 * 构造器
 * 判断是否已满、判断是否空、查看队列数据、显示队列的有效数据个数、入队列、出队列
 */

class CircleQueue {
//    数组的最大容量
    private final int maxSize;
//    front指向队列的第一个元素,初始值为0
    private int front;
//    rear指向队列的最后一个元素的后一个位置,空出一个空间作为约定,初始值为0
    private int rear;
//    存放数据,模拟队列
    private final int[] arr;

    //    创建队列构造器
    public CircleQueue(int maxSize) {
        this.maxSize = maxSize;
        front = 0;
        rear = 0;
        arr = new int[maxSize];
    }

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

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

    //    查看队列数据,显示队列所有数据
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列为空,没有数据!");
            return;
        }
//        从front开始遍历,注意遍历的元素个数
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }

    }

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

    //    显示队列的头数据,注意不是取出数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列是空的,没有数据!");
        }
        return arr[front];
    }

    //    添加数据到队列
    public void addQueue(int n) {
        if (isFull()) {
            System.out.println("队列已满");
            return;
        }
        arr[rear] = n;
        System.out.println(n + "成功添加到队列");
        //        将rear后移,这里必须考虑取模
        rear = (rear + 1) % maxSize;
    }

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

}

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

数据结构--环形队列的介绍与实现 的相关文章

  • 如何使用javac编译java包结构

    我正在尝试编译 从命令行 一个 java 包 该包导入我自己的另一个包 我正在关注一个在线教程 http www roseindia net java master java createsubpackage shtml但当我尝试编译最终的
  • 在 catch 块中重新抛出异常是否有意义?

    从 catch 块中抛出异常只是为了记录消息以便我们确定导致异常的原因是否有意义 Code public void saveLogs Logs logs throws RemoteException try LogsOps saveLogs
  • JDT - 尝试更改类型的超类。我不知道超级类的限定名称

    我有一个程序 除其他任务外 还必须使用 JDT 更改某些类的超类 我有两个字符串 其中包含要交换的超类的限定名称 例如 org example John 和 org example Smith 并且我正在解析整个 AST 搜索扩展这些类的类
  • 如何在 Java 中验证从 Azure AD B2C 生成的 JWT 令牌?

    我正在寻找 Java 代码示例来验证 Azure AD B2C 令牌 我们可以使用哪些依赖项 所有 JWT 令牌的 JWT 令牌验证步骤或代码是否相同还是会有所不同 我们的项目中没有使用 Spring Security 有大量的图书馆her
  • 方法重载。你能过度使用它吗?

    当定义多个使用不同过滤器返回相同形状的数据的方法时 什么是更好的做法 显式方法名称或重载方法 例如 如果我有一些产品并且我正在从数据库中提取 显式方式 public List
  • 如何测试 Jersey REST Web 服务?

    我已经编写了一个 Restful Web 服务 并且必须使用 JUnit4 对其进行测试 我已经使用 Jersey Client 编写了一个客户端 但想知道我是否只能使用 junit4 测试我的服务 至少有人可以帮我提供样品吗 我的休息服务
  • 视频文件转换/转码 Google App Engine

    我想启动一个云计算项目 其简单任务是 接收上传的视频文件 对它们进行一些转码 转换 允许用户下载 流式传输生成的文件 我刚在想ffmpeg作为集成在的外部命令行工具Java Google App engine Application 由于很
  • 如何通过两跳 SSH 隧道使用 JProfiler

    我正在尝试将 JProfiler 连接到在我将调用的服务器上运行的 JVMremote 该服务器只能从我的工作站访问 local 通过我将调用的另一台服务器middle 我的计划是将 JProfiler 连接到remote是这样的 安装 J
  • 如何将 Cucumber 中的数据表转换为对象列表?

    原标题 Java 中的 Cucumber DataTables 中的标量是什么意思 From 参考 Java 提供了几种标量类型 这些包括原始数字 类型 加上布尔值和字符 每个标量 原始 类型都有一个关联的包装类或 参考类型 阅读javad
  • docker 中带有参数的 jar 文件

    Helo 我有一个 java jar 文件 当我从终端运行它时 它会接受一堆参数作为输入 我想制作一个 docker 映像并运行它 其中包含 jar 文件 我仍然可以在其中传递 jar 文件的参数 将 jar 文件设置为您的入口点 http
  • 如何在不使用反射的情况下查看对象是否是数组?

    在Java中如何在不使用反射的情况下查看对象是否是数组 如何在不使用反射的情况下迭代所有项目 我使用 Google GWT 所以不允许我使用反射 我很想在不使用反射的情况下实现以下方法 private boolean isArray fin
  • 如何将点击侦听器添加到 Android/Java Textview 中的字符串中?

    我想要完成的是大多数 Twitter 应用程序中的标准操作 在文本视图中 文本字符串中的单词前面可能有 提及或 主题标签 并且它们实际上能够添加点击侦听器这个词启动了另一项活动 有谁知道这是如何实现的 下面我附上了一张示例照片 显示了我想要
  • 如何使用jdbc驱动编写事务?

    我想使用 jdbc 编写一个事务java 我尝试过这个简单的交易 BEGIN TRANSACTION NL GO NL UPDATE table SET col test where id 1010 NL GO NL COMMIT 我尝试过
  • 如何在 iText 7 中创建页面大小不等的文档

    如何在 iText 7 中创建页面大小不等的文档 iText7 可以吗 在iText5中 我使用document setPageSize and document newPage 如果您通过高级 API 添加内容 Document add
  • FocusEvent 没有获取 JFormattedTextField 的最后一个值,我如何获取它?

    我有两个JFormattedTextField我的物体JFrame目的 我想要通过这些值进行基本数学 加法 JFormattedTextField对象 我希望当焦点丢失第一个或第二个文本字段时发生这种情况 但当 focusLost 事件没有
  • Android 中的自定义相机应用程序问题 - 旋转 270、拉伸捕获视图且未获取所有功能

    我从代码中得到了帮助https github com josnidhin Android Camera Example https github com josnidhin Android Camera Example 但面临一些问题 例如
  • 如何为用户的活动设置计时器?

    如果用户在 5 小时内停止工作 我需要执行特定的方法 假设用户已登录 但他在 5 小时内没有向数据库的特定表添加任何记录 任何时候用户将记录添加到指定的表中 该特定用户的计时器都应该重置 否则它将继续运行 如果达到 5 小时 应用程序应显示
  • 如何在 logback 中启动时滚动日志文件

    我想配置 logback 来执行以下操作 记录到文件 当文件达到 50MB 时滚动文件 仅保留 7 天的日志 启动时始终生成一个新文件 滚动 除了最后一项 启动卷 外 我一切都正常 有谁知道如何实现这一目标 这是配置
  • 无法取消 GWT 中的重复计时器

    我正在尝试在 GWT 中安排一个重复计时器 它将每一毫秒运行一次 轮询某个事件 如果发现满意 则执行某些操作并取消计时器 我尝试这样做 final Timer t new Timer public void run if condition
  • 每次修改代码时都必须 mvn clean install

    我不是来自 Java 世界 但我必须为我的一个项目深入研究它 我不明白为什么每次修改或更新代码时 都必须 mvn clean install 来调试代码的最新版本 你知道为什么吗 尝试按Ctrl Shift F9 热插拔 有时会有所帮助

随机推荐

  • 计算共形几何是计算机科学和,科学网—计算共形几何概览 - 顾险峰的博文

    如果您觉得以下内容比较生疏 不必过于焦虑 请继续关注本公众号 我们将会详尽解释以下所涉及的概念 定理 算法和应用 在未来岁月中 让我们共同学习 共同成长 计算共形几何是计算机科学和纯粹数学之间的交叉学科 其目的是将现代几何 经典几何的概念和
  • 【论文阅读】How transferable are features in deep neural networks?

    研究目标 问题陈述 训练在图像上的深度神经网络 往往前面一层或几层学到的特征都是类似Gabor filters or color blobs的特征 作者叫它们first layer features 这些特征是所有图像所共有的特征 作者叫它
  • gerrit push (change closed)解决办法

    Remember DESKTOP MEFCTAV MINGW64 Desktop VFC vnfres master git push origin HEAD refs for master Enumerating objects 27 d
  • docker 全局日志控制

    vim etc docker daemon json log driver json file log opts max size 1g max file 1 max size 500m 意味着一个容器日志大小上限是500M max fil
  • 网站主题切换

    文章目录 网站主题切换 前言 思路 全部写在 style 属性中 全部写在外部 css 文件中 引用不同的 link 文件 通过 class 命名空间的方式 webpack 插件 webpack theme color replacer 实
  • 【论文翻译+笔记】Neural Machine Reading Comprehension: Methods and Trends

    1 Introduction 过去的MRC技术的特点 hand crafted rules or features 缺点 不能泛化 performance may degrade due to large scale datasets of
  • ADC转换不准确?启用内部参考电压缓冲器 (VREFBUF)

    电压基准缓冲器VREFBUF 一 VREF 描述 1 VDDA 有时与VREF 键合 2 VREF 与 VREF 3 VREF 作用 二 VREFBUF 电压参考缓存器 1 简介 2 功能描述 3 VREFBUF 修边 三 VREFBUF寄
  • 【第40篇】TransFG:用于细粒度识别的 Transformer 架构

    TransFG 用于细粒度识别的 Transformer 架构 摘要 介绍 相关工作 细粒度视觉分类 Transformer 方法 视觉转换器作为特征提取器 TransFG 架构 实验 实验设置 消融研究 定性分析 结论 摘要 论文地址 h
  • stm32cubemx使用mpu6050

    文章目录 接线图 代码 常见问题 接线图 一般情况下 大家买的 mpu 6050 有两种 1 就是 单个的 mpu6050 芯片 2 就是 mpu6050 模块 如果 是第一种情况的话 大家可以参考 下图所示 如果是第二种情况的话 一般来说
  • 简易自动电阻测试仪

    这次练习的题目是2011年的简易自动电阻测试仪 设计并制作一台简易自动电阻测试仪 要求就是测量量程为 100 1k 10k 10M 四档 并且前三档可以自动切档 3 位数字显示 最大显示数必须为 999 能自动显示小数点和单位 测量速率大于
  • Feign简介与简单应用

    一 点睛 Feign是Netflix开发的声明式 模板化的HTTP客户端 Feign可以帮助我们更快捷 优雅地调用HTTP API 在Spring Cloud中 使用Feign非常简单 创建一个接口 并在接口上添加一些注解 代码就完成了 F
  • 注册小鲸鱼88888专用网站

    点击注册充值即可 高效不限速 不限设备 注意这里的地址并没有错 只是你需要想办法正确能进入就行 懂的大佬一定知道用一定的方法访问的 有问题的话可以邮箱 grantwtt 163 com
  • Warning: failed to get default registry endpoint from daemon

    操作系统 CentOS 7 执行命令 docker info docker search docker pull 执行用户 非root 有sudo权限 Docker报错 1 报错现象及原因 2 其它报错 3 配置docker开机自启动 1
  • FFmpeg进阶: 音频变声滤镜

    声音最重要的两个元素就是语速和语调 改变声音的辨识度主要也是从这两方面入手 我们可以通过对音频数据进行插值或者抽值修改 以达到降低语速和增加语速的目的 同时我们也可以通过对数据进行线性拉伸来调节音调 语速调整 语调调整 就可以让我们的声音千
  • QtCreator编译 fatal error: Killed signal terminated program cc1plus问题解决

    原因 编译器消耗的内存超过了系统的限制 强制停止了 解决方式 减少编译时进程数量 make j4
  • 数学建模 层次分析法 python计算权重

    这里用python语言来计算判断矩阵的权重 网上大部分是matlab语言 里面也包含一致性检验的函数 具体各函数使用方法详见代码注释的部分 import numpy as np a np array 1 1 4 2 1 3 4 1 8 2
  • ==和equals的区别

    1 在八种基本类型中 比较的是值的本身 eg public class Damo2 public static void main String args int str 10 int str1 10 System out println
  • ROS navigation的学习和分析

    ROS navigation功能包简单来说就是输入传感器信息和机器人位姿 通过导航算法输出机器人的速度控制指令实现机器人的2D路径规划 贴出代码库 navigation github官方仓库 以下是ROS官方的文档 navigation官方
  • avue-crud 组件,form中实现树形下拉框联动输入框数据,省市区字典联动

    1 需要实现的功能是 当我选择一条数据的时候 后面几个输入框会自动带入 使用的是avue crud组件 参数配置
  • 数据结构--环形队列的介绍与实现

    数据结构 环形队列实现 一 环形队列实现原理 环形队列的几个判断条件 二 代码实现 1 环形队列类 CircleQueue 2 环形队列类测试类 3 程序运行结果 4 完整代码 环形队列可以用数组实现 也可以使用循环链表实现 在使用数组实现