本篇文章,将对队列进行一个深入的解析。

使用场景

队列在日常生活中十分常见,例如:银行排队办理业务、食堂排队打饭等等,这些都是队列的应用。那么队列有什么特点呢?

我们知道排队的原则就是先来后到,排在前面的人就可以优先办理业务,那么队列也一样,队列遵循先进先出的原则。

队列介绍

刚才通过生活中的例子大致了解了一下队列,那么从数据结构的角度来讲,队列到底是什么呢?

  • 队列是一个有序列表,可以用数组或是链表来实现
  • 遵循先进先出的原则,即:先存入队列的数据,先取出;后存入队列的数据,后取出
数组模拟队列

刚才说到,队列可以用数组或是链表来实现。

那我们先来看看用数组如何模拟队列?



队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如上图,其中MaxSize是该队列的最大容量。

因为队列的输出、输入是分别从前后端来处理的,因此需要两个变量front和rear分别记录队列前后端的下标,如上图所示,front会随着数据的输出改变;而rear则是随着数据的输入改变。

继续深入分析:

  • 当我们将数据存入队列时,称为"addQueue",addQueue的处理需要有两个步骤:

    a)将尾指针往后移:rear + 1

    b)若尾指针rear小于队列的最大下标MaxSize - 1,则将数据存入rear所指的数组元素中,否则无法存入数据。rear == MaxSize - 1 时,表示队列已满

分析过后,我们用代码来实现数组模拟队列:

  1. //使用数组模拟队列——编写一个ArrayQueue类
  2. class ArrayQueue {
  3. private int maxSize; // 表示数组的最大容量
  4. private int front; // 队列头
  5. private int rear; // 队列尾
  6. private int[] arr; // 该数组用于存放数据
  7. // 创建队列的构造器
  8. public ArrayQueue(int maxSize) {
  9. this.maxSize = maxSize;
  10. arr = new int[maxSize];
  11. front = -1; // 指向队列头部(指向的是队列头的前一个位置)
  12. rear = -1;// 指向队列尾部(指向的是队列尾的数据)
  13. }
  14. // 判断队列是否满
  15. public boolean isFull() {
  16. return rear == maxSize - 1;
  17. }
  18. // 判断队列是否为空
  19. public boolean isEmpty() {
  20. return rear == front;
  21. }
  22. // 添加数据到队列
  23. public void addQueue(int n) {
  24. // 判断队列是否满
  25. if (isFull()) {
  26. System.out.println("队列满,不能加入数据");
  27. return;
  28. }
  29. rear++;// 让rear后移
  30. arr[rear] = n;
  31. }
  32. // 获取队列的数据
  33. public int getQueue() {
  34. // 判断队列是否空
  35. if (isEmpty()) {
  36. // 抛出异常
  37. throw new RuntimeException("队列空,不能取出数据");
  38. }
  39. front++;// 让front后移
  40. return arr[front];
  41. }
  42. // 显示队列的所有数据
  43. public void showQueue() {
  44. // 遍历
  45. if (isEmpty()) {
  46. System.out.println("队列为空");
  47. return;
  48. }
  49. for (int i : arr) {
  50. System.out.printf("%d\t", i);
  51. }
  52. }
  53. }

经过分析后再来编写代码,会觉得非常简单,现在一个数组模拟的队列就编写完成了。接下来编写测试代码:

  1. public static void main(String[] args) {
  2. // 创建一个队列
  3. ArrayQueue arrayQueue = new ArrayQueue(3);
  4. int key;// 接收用户输入
  5. Scanner sc = new Scanner(System.in);
  6. boolean loop = true;
  7. // 输出一个菜单
  8. while (loop) {
  9. System.out.println();
  10. System.out.println("1:显示队列");
  11. System.out.println("2:添加数据到队列");
  12. System.out.println("3:从队列获取数据");
  13. key = sc.nextInt();// 接收一个字符
  14. switch (key) {
  15. case 1:
  16. arrayQueue.showQueue();
  17. break;
  18. case 2:
  19. System.out.println("输入一个数:");
  20. int value = sc.nextInt();
  21. arrayQueue.addQueue(value);
  22. break;
  23. case 3:
  24. try {
  25. int res = arrayQueue.getQueue();
  26. System.out.println("取出的数据是:" + res);
  27. } catch (Exception e) {
  28. System.out.println(e.getMessage());
  29. }
  30. break;
  31. }
  32. }
  33. }

运行效果如下:



但是,这个程序有一个很大的问题,就是当你把队列中的元素取出来之后,再添加发现添加不了,一直提示队列为空,因为此时指向队列前后端的两个变量相等,而判断队列是否为空就是依靠这两个变量来判断的。

数组模拟环形队列

刚才我们说到这个程序是有问题的,数组只能使用一次,那我们可以对前面的数组模拟队列进行优化,为了能够充分利用数组,我们可将数组看成是一个环形的。

我们先来分析一下(还是看这张图):

  1. 首先我们将front的含义做一个调整:front就指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素,front的初始值为0
  2. 将rear的含义也做一个调整:rear指向队列的最后一个元素的后一个位置,这样调整的目的是希望空出一个空间作为一个约定,rear的初始值也为0
  3. 当队列满时,条件是(rear + 1) % MaxSize == front,那么条件依据是什么呢?我们举个例子,假设现在rear指向的是数组的倒数第二个元素,因为我们要预留出一个空间,所以此时队列应该就是满的,而事实上倒数第二个元素的下标加1然后取模MaxSize确实等于front,因为此时的front为0,就证明该队列已满
  4. 当队列空时,条件是rear == front

那么如果按照上面的分析实现,队列中的有效元素个数即为:(rear + MaxSize - front) % MaxSize。

接下来在原来的代码上进行一个优化:

  1. public class CircleArrayQueueDemo {
  2. public static void main(String[] args) {
  3. // 创建一个队列
  4. CircleArrayQueue arrayQueue = new CircleArrayQueue(3);
  5. int key;// 接收用户输入
  6. Scanner sc = new Scanner(System.in);
  7. boolean loop = true;
  8. // 输出一个菜单
  9. while (loop) {
  10. System.out.println();
  11. System.out.println("1:显示队列");
  12. System.out.println("2:添加数据到队列");
  13. System.out.println("3:从队列获取数据");
  14. key = sc.nextInt();// 接收一个字符
  15. switch (key) {
  16. case 1:
  17. arrayQueue.showQueue();
  18. break;
  19. case 2:
  20. System.out.println("输入一个数:");
  21. int value = sc.nextInt();
  22. arrayQueue.addQueue(value);
  23. break;
  24. case 3:
  25. try {
  26. int res = arrayQueue.getQueue();
  27. System.out.println("取出的数据是:" + res);
  28. } catch (Exception e) {
  29. System.out.println(e.getMessage());
  30. }
  31. break;
  32. }
  33. }
  34. }
  35. }
  36. //使用数组模拟队列——编写一个ArrayQueue类
  37. class CircleArrayQueue {
  38. private int maxSize; // 表示数组的最大容量
  39. private int front; // 队列的第一个元素
  40. private int rear; // 队列的最后一个元素的后一个位置
  41. private int[] arr; // 该数组用于存放数据
  42. // 创建队列的构造器
  43. public CircleArrayQueue(int maxSize) {
  44. this.maxSize = maxSize;
  45. arr = new int[maxSize];
  46. }
  47. // 判断队列是否满
  48. public boolean isFull() {
  49. return (rear + 1) % maxSize == front;
  50. }
  51. // 判断队列是否为空
  52. public boolean isEmpty() {
  53. return rear == front;
  54. }
  55. // 添加数据到队列
  56. public void addQueue(int n) {
  57. // 判断队列是否满
  58. if (isFull()) {
  59. System.out.println("队列满,不能加入数据");
  60. return;
  61. }
  62. // 直接将数据加入
  63. arr[rear] = n;
  64. rear = (rear + 1) % maxSize;// 将rear后移,必须考虑取模(当rear指向最后时,可以通过取模将rear指向队列起始位置)
  65. }
  66. // 获取队列的数据
  67. public int getQueue() {
  68. // 判断队列是否空
  69. if (isEmpty()) {
  70. // 抛出异常
  71. throw new RuntimeException("队列空,不能取出数据");
  72. }
  73. // 需要分析出front是指向队列的第一个元素
  74. // 1、先把front对应的值保存到一个临时变量中
  75. int value = arr[front];
  76. // 2、将front后移,考虑取模
  77. front = (front + 1) % maxSize;
  78. // 3、将临时保存的变量返回
  79. return value;
  80. }
  81. // 显示队列的所有数据
  82. public void showQueue() {
  83. // 遍历
  84. if (isEmpty()) {
  85. System.out.println("队列为空");
  86. return;
  87. }
  88. // 从front开始遍历
  89. for (int i = front; i < front + size(); i++) {
  90. System.out.printf("%d\t", arr[i % maxSize]);
  91. }
  92. }
  93. // 求出当前队列有效数据个数
  94. public int size() {
  95. return (rear + maxSize - front) % maxSize;
  96. }
  97. }

运行效果如下:



现在我们就能够循环利用这个队列了。

图解Java数据结构之队列的更多相关文章

  1. 图解 Java 数据结构

    图解Java数据结构: 一.链表       Java ListNode     https://www.cnblogs.com/easyidea/p/13371863.html 二.栈       ...

  2. Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用

    Java数据结构之---Queue队列 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在 ...

  3. 图解Java数据结构之稀疏数组

    在编程中,算法的重要性不言而喻,没有算法的程序是没有灵魂的.可见算法的重要性. 然而,在学习算法之前我们需要掌握数据结构,数据结构是算法的基础. 我在大学的时候,学校里的数据结构是用C语言教的,因为对 ...

  4. 图解Java数据结构之环形链表

    本篇文章介绍数据结构中的环形链表. 介绍 环形链表,类似于单链表,也是一种链式存储结构,环形链表由单链表演化过来.单链表的最后一个结点的链域指向NULL,而环形链表的建立,不要专门的头结点,让最后一个 ...

  5. java数据结构---循环队列

    #java学习经验总结------循环队列的实现(数组) package datastructure;/*数组实现循环队列 队列first in first out*/ public class Ci ...

  6. Java数据结构——优先级队列

    //================================================= // File Name : PriorityQueue_demo //------------ ...

  7. java数据结构之队列

    队列概述队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作.进行插入操作的端称为队尾,进行删除操作的端称为队头.队列中没有元素时,称为空队列.– ...

  8. Java数据结构之队列(Queue)

    1.使用场景 银行排队的案例: 2.队列介绍 队列是一个有序列表,可以用数组或是链表来实现. 遵循先入先出的原则: 先存入队列的数据,要先取出. 后存入的要后取出 示意图:(使用数组模拟队列示意图) ...

  9. Java数据结构——循环队列

    普通顺序队列存在的问题在普通顺序队列中,入队的操作就是先将尾指针rear右移一个单位,然后将元素值赋值给rear单位.出队时,则是头指针front后移一个单位.像这样进行了一定数量的入队和出队操作后, ...

随机推荐

  1. 解决tensorflow模型保存时Saver报错:TypeError: TF_SessionRun_wrapper: expected all values in input dict to be ndarray

    TypeError: TF_SessionRun_wrapper: expected all values in input dict to be ndarray 对于下面的实际代码: import ...

  2. 简单分析ThreadPoolExecutor回收工作线程的原理

    最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了. 不过,我倒是对线程池是如何回收工 ...

  3. ALLOT流控设备SSG

    Allot AC 系列产品EOL的通知如下. 该产品于2021年3月31日EOL. 替代的产品系列为SG/SSG系列. Allot Secure Service Gateway(SSG)应用程序和用户 ...

  4. linux初学者-网卡的链路聚合篇

    linux初学者-网卡的链路聚合篇 网卡的链路聚合就是将多块网卡连接起来,当一块网卡损坏,网络依旧可以正常运行,可以有效的防止因为网卡损坏带来的损失,同时也可以提高网络访问速度. 网卡的链路聚合一般常 ...

  5. mysql语句汇总

      MySQL常用命令: show databases; 显示数据库 create database name; 创建数据库 use databasename; 选择数据库 drop database ...

  6. Java oop 多态

      1.多态指对象的多种形态:引用多态与方法多态   注意: A:继承是多态的实现基础 B:方法重写也是多态的体现   2.引用多态 A:父类的引用可以指向本类的对象:父类 对象名 = new 父类( ...

  7. ListView 控件总结

     1.ListView类           1.常用的基本属性:         (1)FullRowSelect:设置是否行选择模式.(默认为false) 提示:只有在Details视图该属性才有 ...

  8. rabbitMQ_integeration_spring(七)

    参考了其他博主的资料,整理成完整的代码,直接拷贝就可以测试. 一.创建一个properties文件 mq.host=127.0.0.1 mq.username=root mq.password=roo ...

  9. 「玩转Python」打造十万博文爬虫篇

    前言 这里以爬取博客园文章为例,仅供学习参考,某些AD满天飞的网站太浪费爬虫的感情了. 爬取 使用 BeautifulSoup 获取博文 通过 html2text 将 Html 转 Markdown ...

  10. PHP 防范xss攻击(转载)

    XSS 全称为 Cross Site Scripting,用户在表单中有意或无意输入一些恶意字符,从而破坏页面的表现! 看看常见的恶意字符XSS 输入: 1.XSS 输入通常包含 JavaScript ...