一、什么是环形队列
还记得我们之前数组队列存在的空间浪费问题吗?简单来讲就是:元素出队后的空间无法被二次利用。因为数组队列一直是队尾++。环形队列正是解决此问题的,他存在固定大小,且当队尾指针指向到队列最后位置的时候,再有新元素进来的话会重新放到前面出队的时候预留的空间上。
二、图解环形队列
1、空队列
2、元素A入队
3、元素BCD入队
4、元素AB出队
到目前位置一切正常,也一切如我们所愿,很简单粗暴。那么接下来E元素入队呢?tail指针该何去何从?
5、元素E入队
注意tail指针指向的是索引为0的位置,计算公式为:(tail + 1) % 队列容量
6、元素F入队
7、环形队列总结
- front指向当前队首元素,tail指向最后一个元素的下一个位置
- 入队公式:(tail + 1) % 队列容量
- 出队公式: (front + 1) % 队列容量
- 队空条件:front == tail
- 队满条件:(tail + 1) % 队列容量 == front
- 当前队列有效元素个数:(tail+ 队列容量 - front) % 队列容量;
三、coding
class CircleArray {
// 数组最大容量
private int maxSize;
// front指向队列的第一个元素,也就是说arr[front],初始值为0
private int front;
// rear指向队列的最后一个元素的后一个位置,因为希望空出一个位置作为约定,初始值为0
private int rear;
// 存放数据
private int arr[];
public CircleArray(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
}
// isFull
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
// isEmpty
public boolean isEmpty() {
return front == rear;
}
// addQueue
public void addQueue(int data) {
if (isFull()) {
throw new RuntimeException("full");
}
// 直接将数据加入即可
arr[rear] = data;
// 将rear后移一位,这里必须考虑取模,因为如果rear到最后一位了,但是前面还有空间,所以需要取模,否则数组越界
rear = (rear + 1) % maxSize;
}
// getQueue
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("empty");
}
// 这里需要分析出front是指向队列的第一个元素
// 1.先把front对应的值保存到一个临时变量
// 2.将front后移,取模,否则数组越界
// 3.将临时保存的变量返回
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
// showQueue
public void showQueue() {
if (isEmpty()) {
return;
}
// 从front开始遍历,遍历多少个元素?
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i]);
}
}
// 获取当前队列有效数据的个数
public int size() {
// 1, 3, 0 = 1
return (rear + maxSize - front) % maxSize;
}
// headQueue
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("empty");
}
return arr[front];
}
}