队列
基本概念
队列是一个有序列表,遵循**先进先出(First In First Out)**原则,只允许在一端(队尾)进行插入操作,在另一端(队头)进行取出操作。
实现
可以用数组来模拟队列,由于队列的插入和取出分别在头部和尾部两端进行操作,因此需要两个变量来front和rear分别记录队列的头部和尾部的下标,队列的大小为用maxsize表示。
- front:随着数据的取出而改变,指向队列头部数据的前一个位置
- rear:随着数据的插入而改变,指向队列最后一个元素
示意图:
队列的基本操作
队列初始化
利用构造函数进行队列的初始化操作,应当传入指定大小来创建一个数组
public Queue(int maxSize) {
this.maxSize = maxSize;
this.arr = new int[maxSize];
this.front = -1;
this.rear = -1;
}
判断队列是否为空
队列头部前一个位置下标为front,尾部下标为rear,当取出元素时队列的front往后移动一格,因此当front == rear时,队列为空。
public boolean isEmpty() {
return front == rear;
}
判断队列是否已满
队列的最大容量为maxsize,因此队列的最大下标为maxsize-1。所以,当队尾下标rear == maxsize-1时,队列已满。
public boolean isFull() {
return rear == maxSize - 1;
}
数据入队
- 先判断队列是否已满,如果满了,就不能让数据插入
- 数据入队时,只需要移动尾部,头部保持不动。
- 先将尾部下标往后移动:rear = rear + 1,然后将数据插入数据尾部位置。
public void enQueue(int element) {
if (isFull()) {
System.out.println("队列已满,不能插入!");
} else {
rear = rear + 1;
arr[rear] = element;
}
}
数据出队
- 先判断队列是否为空,如果为空,就不能取出数据
- 数据出队,只需要移动头部,尾部保持不动
- 将头部下标移动到要出队的数据的位置,然后将该数据返回
public int deQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据!!");
} else {
front = front + 1;
return arr[front];
}
}
打印队列
public void printQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据!!");
} else {
for (int i = front + 1; i <= rear; i++) {
System.out.print(arr[i] + "\t");
}
}
System.out.println();
}
队列的假溢出
示意图中的第三个,随着入队和出队的操作,队列组件向后移动。当rear = maxsize-1时,队列已满。但此时队列中已经出队了两个,前面两个位置是空着的,但此时已然判断队列已经满了,这就是队列的假溢出。
循环队列
为了解决队列的假溢出,要对上面的队列进行优化,通过取模的方式来判断队列是否已满。对front和rear进行调整。
改进思路:
- front:指向队列的第一个元素,初始值为 0
- rear:初始值为0,指向队列的最后一个元素的后一个位置,空出一个位置做区分,而用于避免当rear和front指向同一个位置时,无法判断队列是空还是已满。
- 此时,队列的最大容量为maxsize-1
- 当rear==front时,队列为空
- 当(rear+1)%maxsize==front时,队列已满
- 此时,队列中的实际存储数据为:(rear+maxsize-front)%maxsize
代码实现
public class CircleQueue {
private int maxSize;// 队列大小,实际存放数据大小maxsize-1
private int[] arr;
private int front;// 队头,指向第一个元素
private int rear;// 队尾,指向最后一个元素后一个位置
public CircleQueue(int maxSize) {
this.maxSize = maxSize;
this.arr = new int[maxSize];
this.front = 0;
this.rear = 0;
}
/**
* 判断是否为空
*
* @return
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 判断队列是否满
*
* @return
*/
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
/**
* 入队操作
*/
public void enQueue(int element) {
if (isFull()) {
System.out.println("队列已满,不能插入!");
} else {
//rear指向最后一个数据的后一个位置,
//先在当前指向位置插入数据,再将rear向后移动
arr[rear] = element;
rear = (rear + 1) % maxSize;
}
}
/**
* 元素出队
*/
public int deQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据!!");
} else {
//front指向队头数据,应当先取出数据,再讲front向后移动。
int element = arr[front];
front = (front + 1) % maxSize;
return element;
}
}
/**
* 查看队列头元素
*
* @return
*/
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据!!");
} else {
return arr[front];
}
}
/**
* 打印队列
*/
public void printQueue() {
if (isEmpty()) {
throw new RuntimeException("队列中没有数据!!");
} else {
for (int i = front; i < front + size(); i++) {
System.out.print(arr[i % maxSize] + " ");
}
}
System.out.println();
}
/**
* 队列大小
*
* @return
*/
public int size() {
return (rear + maxSize - front) % maxSize;
}
}