关于队列,首先要知道的一点就是,队列是一个先进先出的数据结构。
我们可以使用数组实现队列,也可以使用链表实现队列。本文将的是数组队列
普通队列
定义
普通队列的定义很简单,我们首先知道一个定义
- front 代表队首指针,指向的是队列中第一个元素的前一个位置,默认为 -1
- rear 代表队尾指针,指向队列中最后一个元素,默认为-1
- maxSize 代表队列的最大容量
- data[] 用来存放队列数据的数组
至于为什么要让 front 指向第一个元素的前一个位置呢?
举个🌰
假设我们现在有3 个元素
这个时候如果1出队,front++ 变成了0
2出队,front++ 变成了1
然后我们可以直接 return data[front] 就可以返回出队的元素了,而且出队,front++也更容易让我们理解这代表出队了
满队列 和 空队列的判断
如何判断队列为空?
当 rear == front 的时候,队列为空
初始情况下,front == rear == -1
全部元素出队的情况下:
front 仍然与 rear 相等
如何判断队列为满?
rear + 1 = maxSize的情况下判断队列为满
要记得,数组的下标是从0开始数的,所以,如图中,队列中存在3个元素,那么rear = 2
所以 rear + 1 = maxSize 为满队列
代码实现
public 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("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':queue.showQueue();break;case 'a':System.out.println("输入一个值");int value = scanner.nextInt();queue.addQueue(value);break;case 'g':try {int res = queue.getQueue();System.out.printf("取出的元素为%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'h':try {int res = queue.headQueue();System.out.printf("队首的元素为%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'e':scanner.close();loop = false;break;default:break;}}System.out.println("退出程序");}}//使用数组模拟队列class ArrayQueue {//队列的最大容量private int maxSize;//队首private int front;//队尾private int rear;//存放的数据private int[] data;//创建一个队列public ArrayQueue(int maxSize) {this.maxSize = maxSize;data = new int[maxSize];front = -1;//指向队列头部的前一个位置rear = -1;//指向队尾(指向队列的最后一个数据)}//添加数据到队列public void addQueue(int n) {if (isFull()) {System.out.println("队列已满");return;}//让队尾指针后移rear++;data[rear] = n;}//获取队列元素(出队列)public int getQueue() {if (isEmpty()) {throw new RuntimeException("队列为空,不能取数据");}//队首指针后移front++;return data[front];}//遍历队列的元素public void showQueue() {if (isEmpty()) {System.out.println("队列为空,没有数据");return;}for (int i = front+1; i <= rear; i++) {System.out.println(data[i]);}}//显示队列的头数据public int headQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,不能取数据");}return data[front+1];}//判断队列是否满了public boolean isFull() {//如果队尾 == 最大容量-1 那么说明队列为空return maxSize-1 == rear;}public boolean isEmpty() {//队首==队尾时 说明队列为空return front == rear;}}
环形队列
环形队列与普通队列相比,有什么优点呢?
如果在普通队列中,全部元素全部出队,这个时候 rear和 front指针都指向了最后一个空间
如果我们不让front 和 rear 重新初始化,那么这个队列就不能够循环使用了
为了解决这个问题,我们提出了环形队列的概念
定义
首先在定义这方面,相比于普通队列,我们做了一个改变
- front 队首指针,指向队列中第一个元素,默认为0
- rear 队尾指针,指向队列中最后一个元素的后一个位置,默认为0
- 我们要预留一个空间,用来判断队满还是队空
所以我们的实际的最大有效元素个数为 maxSize - 1
满队列 和 空队列的判断
首先要明白最最最最重要的一点,我们的预留空间是动态变化的
因为是环形队列,所以存在 rear指针的下标 可能会比front 还小
如果,当满队列后
front的下标为0
rear的下标为 3
如果这个时候1出队,然后4入队,这个时候
那么 front 变成了1
rear变成了0,就出现了 rear比front 小的情况
如图(❌为预留空间,不存放元素)
所以,计算有效个数的时候我们要考虑这种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仍然表示队列为空的情况,没发生改变。
代码实现
public class CircleArrayQueueDemo {public static void main(String[] args) {CircleArrayQueue queue = 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): 显示队头元素");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 res = queue.getQueue();System.out.printf("取出的元素为%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'h':try {int res = queue.headQueue();System.out.printf("队首的元素为%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case 'e':scanner.close();loop = false;break;default:break;}}System.out.println("退出程序");}}//使用数组模拟队列class CircleArrayQueue {//队列的最大容量private int maxSize;//队首private int front;//队尾private int rear;//存放的数据private int[] data;//创建一个队列public CircleArrayQueue(int maxSize) {this.maxSize = maxSize;data = new int[maxSize];front = 0;//指向队列中第一个元素rear = 0;//指向队列中最后一个元素的后一个位置}//添加数据到队列public void addQueue(int n) {if (isFull()) {System.out.println("队列已满");return;}//先添加元素data[rear] = n;//再让队尾指针后移//但是要注意是环形队列,所以我们可以对 maxSize求余rear = (rear + 1) % maxSize;}//获取队列元素(出队列)public int getQueue() {if (isEmpty()) {throw new RuntimeException("队列为空,不能取数据");}//这里一定要注意啊,因为我们的 队首指针也是会环形变化的//所以我们可以先把值拿出来,然后再对指针进行操作//队首指针后移int value = data[front];front = (front + 1) % maxSize;return value;}//遍历队列的元素public void showQueue() {if (isEmpty()) {System.out.println("队列为空,没有数据");return;}//这里我们可以直接遍历 有效元素个数。//而且还有一点,在环形队列中 i 也是有可能大于 最大下标的for (int i = front; i < front + size(); i++) {System.out.println(data[(i % maxSize)]);}}//判断队列中的有效元素个数public int size() {return (rear + maxSize - front) % maxSize;}//显示队列的头数据public int headQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,不能取数据");}return data[front];}//判断队列是否满了public boolean isFull() {return (rear + 1) % maxSize == front;}public boolean isEmpty() {//队首==队尾时 说明队列为空return front == rear;}}
