1. 使用场景

银行排队的案例:
image.png

2. 基本介绍

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

3. 数组模拟队列

思路

队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 frontrear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:
image.png
当我们将数据存入队列时称为”addQueue“,addQueue的处理需要有两个步骤:思路分析
1) 将尾指针往后移:rear+1 , 当 front == rear 【空】
2) 若尾指针 rear小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。rear == maxSize - 1 [队列满]

不足

  • 数组只能使用一次,当队列中的数据取出后,并不能复用之前用过的空间。
  • 优化参考环形队列

    实现

  • 添加元素,改变队尾指针 rear 的值;

  • 移出元素,改变队头指针 front 的值;
  • 其他操作不改变队头队尾的值,只在值基础上运算(取出队头为:front + 1)

    数组队列

    ```java package com.zsy.datastructure.queue;

/**

  • 使用数组模拟队列 *
  • @author zhangshuaiyin */ public class ArrayQueue {

    /**

    • 队列最大空间 / private int maxSize; /*
    • front: 指向队列头的前一个位置,用于标识队列头
    • rear:指向队列尾部 */ private int front; private int rear; private int[] array;

      public ArrayQueue(int arrayMaxSize) { maxSize = arrayMaxSize; array = new int[maxSize]; front = -1; rear = -1; }

      /**

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

      /**

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

      /**

    • 向队列添加元素 *
    • @param item 元素 */ public void add(int item) { if (isFull()) {

      1. System.out.println("队列已满,不能添加元素~~");
      2. return;

      } // rear 后移,新增一个存储空间 rear++; array[rear] = item; }

      /**

    • 出队 *
    • @return 头部数据 */ public int remove() { if (isEmpty()) {

      1. throw new RuntimeException("队列为空不能取出数据!!");

      } return array[++front]; }

      /**

    • 打印队列中的元素 */ public void show() { if (isEmpty()) {

      1. System.out.println("The queue is empty.");
      2. return;

      } for (int i = 0; i < array.length; i++) {

      1. System.out.printf("arr[%d]=%d\n", i, array[i]);

      } }

      /**

    • 打印队列头部元素 */ public void head() { if (isEmpty()) {
      1. System.out.println("The queue is empty.");
      2. return;
      } System.out.println(“head: “ + array[front + 1]); } } ```

      测试

      ```java package com.zsy.datastructure.queue;

import java.util.Scanner;

/**

  • @author zhangshuaiyin */ public class ArrayQueueTest { public static void main(String[] args) {

    1. ArrayQueue queue = new ArrayQueue(3);
    2. // 接收输入字符
    3. char key;
    4. Scanner scanner = new Scanner(System.in);
    5. boolean loop = true;
    6. while (loop) {
    7. System.out.println("****************");
    8. System.out.println("s(show): 显示队列");
    9. System.out.println("a(add): 添加数据");
    10. System.out.println("g(get): 取出数据");
    11. System.out.println("h(head): 头部数据");
    12. System.out.println("按其他任意键退出...");
    13. System.out.println("****************");
    14. key = scanner.next().charAt(0);
    15. switch (key) {
    16. case 's':
    17. queue.show();
    18. break;
    19. case 'a':
    20. System.out.println("请输入要添加队列的数字:");
    21. int value = scanner.nextInt();
    22. queue.add(value);
    23. break;
    24. case 'g':
    25. try {
    26. System.out.println("取出的数据:" + queue.remove());
    27. } catch (Exception e) {
    28. System.out.println(e.getMessage());
    29. }
    30. break;
    31. case 'h':
    32. queue.head();
    33. break;
    34. default:
    35. scanner.close();
    36. loop = false;
    37. System.out.println("程序退出~~");
    38. break;
    39. }
    40. }

    } } ```

    4. 数组循环队列

    参考《大话数据结构》

    image.pngimage.pngimage.png

    思路分析

  • front:指向队列的第一个元素,初始值为0;
  • rear:指向队列最后一个元素的下一个位置,且该位置为保留位,不会被数据占用;
  • 当队列满时,rear 应该指向 front 的前一个位置,即 (rear + 1) % maxSize == front();

image.png

  • 当队列为空时,front == rear,即出队时 front 后移,当移动到保留位置,表示队列空;
  • 队列中的元素个数:(rear - front + maxSize) % maxSize
    • 当rear > front,长度为:rear - front

image.pngimage.png

  • 当 rear < front,长度为:(0 + rear) + (maxSize - front) = rear - front + maxSize

image.pngimage.png

代码实现:

循环队列

  1. package com.zsy.datastructure.queue;
  2. /**
  3. * 数组实现循环队列
  4. *
  5. * @author zhangshuaiyin
  6. */
  7. public class CircleArrayQueue {
  8. /**
  9. * 数组最大长度,因为rear预留位,队列长度会少一位
  10. */
  11. private int maxSize;
  12. /**
  13. * front: 指向队列头一个元素
  14. */
  15. private int front;
  16. /**
  17. * rear: 指向队列最后一个元素的下一个位置
  18. * rear指向的位置为保留位,不会被元素占用
  19. */
  20. private int rear;
  21. private int[] array;
  22. public CircleArrayQueue(int arrayMaxSize) {
  23. this.maxSize = arrayMaxSize;
  24. array = new int[maxSize];
  25. this.front = this.rear = 0;
  26. }
  27. /**
  28. * @return 判断队列是否为空
  29. */
  30. public boolean isEmpty() {
  31. return front == rear;
  32. }
  33. /**
  34. * @return 判断队列是否已满
  35. */
  36. public boolean isFull() {
  37. return (rear + 1) % maxSize == front;
  38. }
  39. /**
  40. * 向队列添加元素
  41. *
  42. * @param item 元素
  43. */
  44. public void add(int item) {
  45. if (isFull()) {
  46. System.out.println("队列已满,不能添加元素~~");
  47. return;
  48. }
  49. array[rear] = item;
  50. // rear 后移,当移动到最后一个位置时,从 0 重新计数
  51. rear = (rear + 1) % maxSize;
  52. }
  53. /**
  54. * 出队
  55. *
  56. * @return 头部数据
  57. */
  58. public int remove() {
  59. if (isEmpty()) {
  60. throw new RuntimeException("队列为空不能取出数据!!");
  61. }
  62. // 取出数据为:array[front], 取出后 front 后移,当移动到最后一个位置时,从 0 重新计数
  63. int value = array[front];
  64. front = (front + 1) % maxSize;
  65. return value;
  66. }
  67. /**
  68. * 打印队列中的元素,从front开始到最后一个元素
  69. */
  70. public void show() {
  71. if (isEmpty()) {
  72. System.out.println("The queue is empty.");
  73. return;
  74. }
  75. for (int i = front; i < front + size(); i++) {
  76. System.out.printf("arr[%d]=%d\n", i % maxSize, array[i % maxSize]);
  77. }
  78. }
  79. /**
  80. * 打印队列头部元素
  81. */
  82. public void head() {
  83. if (isEmpty()) {
  84. System.out.println("The queue is empty.");
  85. return;
  86. }
  87. System.out.println("head: " + array[front]);
  88. }
  89. /**
  90. * @return 获取队列数据个数
  91. */
  92. public int size() {
  93. return (rear - front + maxSize) % maxSize;
  94. }
  95. }

测试

参考:数组队列测试

  1. package com.zsy.datastructure.queue;
  2. import java.util.Scanner;
  3. /**
  4. * @author zhangshuaiyin
  5. */
  6. public class CircleArrayQueueTest {
  7. public static void main(String[] args) {
  8. CircleArrayQueue queue = new CircleArrayQueue(3);
  9. // 接收输入字符
  10. char key;
  11. Scanner scanner = new Scanner(System.in);
  12. boolean loop = true;
  13. while (loop) {
  14. System.out.println("****************");
  15. System.out.println("s(show): 显示队列");
  16. System.out.println("a(add): 添加数据");
  17. System.out.println("g(get): 取出数据");
  18. System.out.println("h(head): 头部数据");
  19. System.out.println("按其他任意键退出...");
  20. System.out.println("****************");
  21. key = scanner.next().charAt(0);
  22. switch (key) {
  23. case 's':
  24. queue.show();
  25. break;
  26. case 'a':
  27. System.out.println("请输入要添加队列的数字:");
  28. int value = scanner.nextInt();
  29. queue.add(value);
  30. break;
  31. case 'g':
  32. try {
  33. System.out.println("取出的数据:" + queue.remove());
  34. } catch (Exception e) {
  35. System.out.println(e.getMessage());
  36. }
  37. break;
  38. case 'h':
  39. queue.head();
  40. break;
  41. default:
  42. scanner.close();
  43. loop = false;
  44. System.out.println("程序退出~~");
  45. break;
  46. }
  47. }
  48. }
  49. }