关于队列,首先要知道的一点就是,队列是一个先进先出的数据结构。
我们可以使用数组实现队列,也可以使用链表实现队列。本文将的是数组队列

普通队列

定义

普通队列的定义很简单,我们首先知道一个定义

  • front 代表队首指针,指向的是队列中第一个元素的前一个位置,默认为 -1
  • rear 代表队尾指针,指向队列中最后一个元素,默认为-1
  • maxSize 代表队列的最大容量
  • data[] 用来存放队列数据的数组

至于为什么要让 front 指向第一个元素的前一个位置呢?
举个🌰
假设我们现在有3 个元素
image.png
这个时候如果1出队,front++ 变成了0
2出队,front++ 变成了1
然后我们可以直接 return data[front] 就可以返回出队的元素了,而且出队,front++也更容易让我们理解这代表出队了

满队列 和 空队列的判断

如何判断队列为空?

当 rear == front 的时候,队列为空
初始情况下,front == rear == -1
全部元素出队的情况下:
image.png
front 仍然与 rear 相等

如何判断队列为满?

rear + 1 = maxSize的情况下判断队列为满
要记得,数组的下标是从0开始数的,所以,如图中,队列中存在3个元素,那么rear = 2
所以 rear + 1 = maxSize 为满队列

代码实现

  1. public class ArrayQueueDemo {
  2. public static void main(String[] args) {
  3. ArrayQueue queue = new ArrayQueue(3);
  4. char key = ' ';
  5. Scanner scanner = new Scanner(System.in);//
  6. boolean loop = true;
  7. while(loop) {
  8. System.out.println("s(show): 遍历队列");
  9. System.out.println("e(exit): 退出程序");
  10. System.out.println("a(add): 添加一个数据到队列");
  11. System.out.println("g(get): 从队列中取出一个数据");
  12. System.out.println("h(head): 显示队头元素");
  13. key = scanner.next().charAt(0);
  14. switch (key) {
  15. case 's':
  16. queue.showQueue();
  17. break;
  18. case 'a':
  19. System.out.println("输入一个值");
  20. int value = scanner.nextInt();
  21. queue.addQueue(value);
  22. break;
  23. case 'g':
  24. try {
  25. int res = queue.getQueue();
  26. System.out.printf("取出的元素为%d\n", res);
  27. } catch (Exception e) {
  28. System.out.println(e.getMessage());
  29. }
  30. break;
  31. case 'h':
  32. try {
  33. int res = queue.headQueue();
  34. System.out.printf("队首的元素为%d\n", res);
  35. } catch (Exception e) {
  36. System.out.println(e.getMessage());
  37. }
  38. break;
  39. case 'e':
  40. scanner.close();
  41. loop = false;
  42. break;
  43. default:
  44. break;
  45. }
  46. }
  47. System.out.println("退出程序");
  48. }
  49. }
  50. //使用数组模拟队列
  51. class ArrayQueue {
  52. //队列的最大容量
  53. private int maxSize;
  54. //队首
  55. private int front;
  56. //队尾
  57. private int rear;
  58. //存放的数据
  59. private int[] data;
  60. //创建一个队列
  61. public ArrayQueue(int maxSize) {
  62. this.maxSize = maxSize;
  63. data = new int[maxSize];
  64. front = -1;//指向队列头部的前一个位置
  65. rear = -1;//指向队尾(指向队列的最后一个数据)
  66. }
  67. //添加数据到队列
  68. public void addQueue(int n) {
  69. if (isFull()) {
  70. System.out.println("队列已满");
  71. return;
  72. }
  73. //让队尾指针后移
  74. rear++;
  75. data[rear] = n;
  76. }
  77. //获取队列元素(出队列)
  78. public int getQueue() {
  79. if (isEmpty()) {
  80. throw new RuntimeException("队列为空,不能取数据");
  81. }
  82. //队首指针后移
  83. front++;
  84. return data[front];
  85. }
  86. //遍历队列的元素
  87. public void showQueue() {
  88. if (isEmpty()) {
  89. System.out.println("队列为空,没有数据");
  90. return;
  91. }
  92. for (int i = front+1; i <= rear; i++) {
  93. System.out.println(data[i]);
  94. }
  95. }
  96. //显示队列的头数据
  97. public int headQueue() {
  98. if(isEmpty()) {
  99. throw new RuntimeException("队列为空,不能取数据");
  100. }
  101. return data[front+1];
  102. }
  103. //判断队列是否满了
  104. public boolean isFull() {
  105. //如果队尾 == 最大容量-1 那么说明队列为空
  106. return maxSize-1 == rear;
  107. }
  108. public boolean isEmpty() {
  109. //队首==队尾时 说明队列为空
  110. return front == rear;
  111. }
  112. }

环形队列

环形队列与普通队列相比,有什么优点呢?
如果在普通队列中,全部元素全部出队,这个时候 rear和 front指针都指向了最后一个空间
image.png
如果我们不让front 和 rear 重新初始化,那么这个队列就不能够循环使用了
为了解决这个问题,我们提出了环形队列的概念

定义

首先在定义这方面,相比于普通队列,我们做了一个改变

  • front 队首指针,指向队列中第一个元素,默认为0
  • rear 队尾指针,指向队列中最后一个元素的后一个位置,默认为0
  • 我们要预留一个空间,用来判断队满还是队空

所以我们的实际的最大有效元素个数为 maxSize - 1

满队列 和 空队列的判断

首先要明白最最最最重要的一点,我们的预留空间是动态变化的

因为是环形队列,所以存在 rear指针的下标 可能会比front 还小
如果,当满队列后
front的下标为0
rear的下标为 3
如果这个时候1出队,然后4入队,这个时候
那么 front 变成了1
rear变成了0,就出现了 rear比front 小的情况
如图(❌为预留空间,不存放元素)
image.png
所以,计算有效个数的时候我们要考虑这种raer 比 front 小的情况
环形队列中的有效元素个数的公式为
(rear + maxSize - front ) % maxSize

我们可以做一个计算,当图左的时候,front = 0,rear =3
(3 + 4 -0) % 4 = 3
图右的时候,front = 1, rear = 0
(0 + 4 - 1) % 4 = 3

那么如何判断是否为 满队列呢?
我们可以通过
(rear + 1) % maxSize = front
% maxSize 是为了当出现图2的时候,我们仍然可以计算出 队列是否为满
这个公式来判断队列是否为空,具体可以进行计算
图左 rear 的下标3, front = 0
(3 + 1 )% 4 = 0 == front
这个时候队列为满
图右
rear = 0,front =1
(0 + 1 )% 4 = 1 == front

同样的,front == rear仍然表示队列为空的情况,没发生改变。

代码实现

  1. public class CircleArrayQueueDemo {
  2. public static void main(String[] args) {
  3. CircleArrayQueue queue = new CircleArrayQueue(4);
  4. char key = ' ';
  5. Scanner scanner = new Scanner(System.in);//
  6. boolean loop = true;
  7. while(loop) {
  8. System.out.println("s(show): 遍历队列");
  9. System.out.println("e(exit): 退出程序");
  10. System.out.println("a(add): 添加一个数据到队列");
  11. System.out.println("g(get): 从队列中取出一个数据");
  12. System.out.println("h(head): 显示队头元素");
  13. key = scanner.next().charAt(0);
  14. switch (key) {
  15. case 's':
  16. queue.showQueue();
  17. break;
  18. case 'a':
  19. System.out.println("输入一个值");
  20. int value = scanner.nextInt();
  21. queue.addQueue(value);
  22. break;
  23. case 'g':
  24. try {
  25. int res = queue.getQueue();
  26. System.out.printf("取出的元素为%d\n", res);
  27. } catch (Exception e) {
  28. System.out.println(e.getMessage());
  29. }
  30. break;
  31. case 'h':
  32. try {
  33. int res = queue.headQueue();
  34. System.out.printf("队首的元素为%d\n", res);
  35. } catch (Exception e) {
  36. System.out.println(e.getMessage());
  37. }
  38. break;
  39. case 'e':
  40. scanner.close();
  41. loop = false;
  42. break;
  43. default:
  44. break;
  45. }
  46. }
  47. System.out.println("退出程序");
  48. }
  49. }
  50. //使用数组模拟队列
  51. class CircleArrayQueue {
  52. //队列的最大容量
  53. private int maxSize;
  54. //队首
  55. private int front;
  56. //队尾
  57. private int rear;
  58. //存放的数据
  59. private int[] data;
  60. //创建一个队列
  61. public CircleArrayQueue(int maxSize) {
  62. this.maxSize = maxSize;
  63. data = new int[maxSize];
  64. front = 0;//指向队列中第一个元素
  65. rear = 0;//指向队列中最后一个元素的后一个位置
  66. }
  67. //添加数据到队列
  68. public void addQueue(int n) {
  69. if (isFull()) {
  70. System.out.println("队列已满");
  71. return;
  72. }
  73. //先添加元素
  74. data[rear] = n;
  75. //再让队尾指针后移
  76. //但是要注意是环形队列,所以我们可以对 maxSize求余
  77. rear = (rear + 1) % maxSize;
  78. }
  79. //获取队列元素(出队列)
  80. public int getQueue() {
  81. if (isEmpty()) {
  82. throw new RuntimeException("队列为空,不能取数据");
  83. }
  84. //这里一定要注意啊,因为我们的 队首指针也是会环形变化的
  85. //所以我们可以先把值拿出来,然后再对指针进行操作
  86. //队首指针后移
  87. int value = data[front];
  88. front = (front + 1) % maxSize;
  89. return value;
  90. }
  91. //遍历队列的元素
  92. public void showQueue() {
  93. if (isEmpty()) {
  94. System.out.println("队列为空,没有数据");
  95. return;
  96. }
  97. //这里我们可以直接遍历 有效元素个数。
  98. //而且还有一点,在环形队列中 i 也是有可能大于 最大下标的
  99. for (int i = front; i < front + size(); i++) {
  100. System.out.println(data[(i % maxSize)]);
  101. }
  102. }
  103. //判断队列中的有效元素个数
  104. public int size() {
  105. return (rear + maxSize - front) % maxSize;
  106. }
  107. //显示队列的头数据
  108. public int headQueue() {
  109. if(isEmpty()) {
  110. throw new RuntimeException("队列为空,不能取数据");
  111. }
  112. return data[front];
  113. }
  114. //判断队列是否满了
  115. public boolean isFull() {
  116. return (rear + 1) % maxSize == front;
  117. }
  118. public boolean isEmpty() {
  119. //队首==队尾时 说明队列为空
  120. return front == rear;
  121. }
  122. }