1. 简单介绍
- 队列是一个有序列表,可以用数组或是链表来实现
- 遵循先入先出的原则。即:先存入队列的数据,要先取出;后存入的要后取出
- 一个应用场景:排队
2. 数组模拟队列
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变。
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
- 当我们将数据存入队列时称为"addQueue",addQueue 的处理需要有 2 个步骤
- 将尾指针往后移:rear+1,当 front == rear [队列空]
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。 rear == maxSize - 1 [队列满]
3. 代码实现
public class ArrayQueue {
// 表示数组最大容量
private int maxSize;
// 队列首元素之前的元素的下标
private int front;
// 队列尾元素下标
private int rear;
// 用来存放数据(模拟队列)
private int[] arr;
// 队列构造器
public ArrayQueue(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
// 队列首有效元素的前一个位置的索引
front = -1;
// 队列尾最后一个有效元素的索引
rear = -1;
}
// 判断队列是否满
public boolean isFull() {
return rear == maxSize-1;
}
// 判断队列是否空
public boolean isEmpty() {
return rear == front;
}
// 入列
public void addQueue(int val) {
// 判断是否满
if (isFull()) {
System.out.println("队列满");
return;
}
arr[++rear] = val;
}
// 出列
public int getQueue() {
// 判断是否空
if (isEmpty()) throw new RuntimeException("队列空");
return arr[++front];
}
// 打印队列数据
public void showQueue() {
if (isEmpty()) {
System.out.println("队列空");
return;
}
for (int i = 0; i < maxSize; i++)
System.out.printf("arr[%d] = %d
", i, arr[i]);
}
// 显示首有效元素(不是取)
public int headQueue() {
if (isEmpty()) throw new RuntimeException("队列空");
return arr[front + 1];
}
}
4. 进阶:循环队列
上面的代码还有些问题,数组不能复用,造成内存浪费。使用传统数组来构造队列,无论是出队还是入队都是指针在往后移;那么,之前的出队元素所在的数组元素空间就全被浪费了,所以必须让它成个"⚪"。即 当指针指向「end 元素」,再往后移,就会又循环回来,指向了 「first 元素」。
- 队列初始化:front 和 rear 的值都是 0
- 队列非空
- front 指向队列的第一个有效元素
- rear 指向队列最后一个有效元素的下一个元素
- 队列空:front == rear(不一定非得都等于 0)
- 队列满:队列最多存放的有效数据个数:array.length - 1。即:当 (rear+1) % length == front,表示队列已满
- 队列有效元素个数:count = (rear - front + maxSize) % maxSize
- 当 rear > front,相减得到的就是 {有效元素个数},加 maxSize 再取余还是它本身
- 当 rear < front,相减得到的是 [ - {空闲位置数} ],加上 maxSize ,就正好是 {有效元素个数} ,再取余,还是 {有效元素个数}
5. 代码优化
public class CircleArrayQueue {
// 表示数组最大容量
private int maxSize;
// 队列首元素下标
private int front;
// 队列尾元素的后一个位置的下标
private int rear;
// 用来存放数据(模拟队列)
private int[] arr;
// 队列构造器
public CircleArrayQueue(int maxSize) {
this.maxSize = maxSize + 1;
// 因为需要保留其中一个位置作为 [标识位], 故创建的数组大小要大一个元素空间
arr = new int[maxSize + 1];
// 初始化
front = 0;
rear = 0;
}
// 判断队列是否满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
// 判断队列是否空
public boolean isEmpty() {
return rear == front;
}
// 入列
public void addQueue(int val) {
// 判断是否满
if (isFull()) {
System.out.println("队列满");
return;
}
arr[rear] = val;
// rear 后移时要考虑到:rear 指向的是数组末尾位置
rear = (rear + 1) % maxSize;
}
// 出列
public int getQueue() {
// 判断是否空
if (isEmpty()) throw new RuntimeException("队列空");
int val = arr[front];
// front 后移时要考虑到:front 指向的是数组末尾位置
front = (front + 1) % maxSize;
return val;
}
// 当前队列有效元素个数
public int getCount() {
return (rear - front + maxSize) % maxSize;
}
// 打印队列数据
public void showQueue() {
if (isEmpty()) {
System.out.println("队列空");
return;
}
// 思路:从front开始遍历,遍历count个元素
int count = getCount();
for (int i = front; i < front + count; i++)
System.out.printf("arr[%d] = %d
", i%maxSize, arr[i%maxSize]);
}
// 显示首有效元素(不是取)
public int headQueue() {
if(isEmpty()) throw new RuntimeException("队列空");
return arr[front];
}
}