队列介绍
- 队列是一个有序列表,可以使用数组或是链表来实现
- 队列遵循先入先出的原则,即先存入的数据要先取出,后存入的数据要后取出
- 示意图:
数组模拟队列思路
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中
maxSize
是该队列的最大容量 - 队列的输入、输出是分别是从队列的前后端来处理(即加入数据从队列前端加入,取出数据从队列后端取出),一次需要两个变量来记录队列前后端的下标(
front
和rear
)。其中front
会随着数据的输入而改变,rear
会随着队列的输出而改变
- 队列的基本操作有:初始化队列、入队操作、出队操作、读队头元素、判队空操作(判断队列是否为空)
顺序队列
基本思路分析:
- 判断队列已满的条件是:
rear==maxSize-1
public boolean isFull() {
return rear == maxSize - 1;
}
- 判断队列为空的条件是:
rear==front
public boolean isEmpty() {
return rear == front;
}
- 我们将入队列的方法称之为
addQueue()
,其实现需要有两个步骤- 将尾指针后移:
rear+1
; - 若尾指针
real
小于队列的最大下标maxSize-1
,则将数据存入rear
指向的数组元素中。当rear==maxSize-1
时队列满,不能存入数据
- 将尾指针后移:
public void addQueue(int n) {
//判断队列是否已满,已满则不能添加数据
if (isFull()) {
System.out.println("队列已满,不能添加数据");
return;
}
arr[++rear] = n; //先将rear后移,在将数据加入队列
}
- 如队列需要两个步骤
- 判断队列是否为空,若为空则不能出队列
- 队列不为空,则先将
front
后移在输出其指向的数据(front
指向的是头数据的前一个位置)
public int getQueue() {
//判断队列为空,若为空这抛出异常
if (isEmpty()) {
throw new RuntimeException("队列为空,不能取数据");
}
return arr[++front];//先将front后移使其指向要出队列的元素,然后返回其指向的元素
}
- 读取队列头数据不是取数头数据
public int headQueue() {
//判断队列是否为空,为空则无头数据
if (isEmpty()) {
throw new RuntimeException("队列为空,没有数据");
}
return a[front + 1];//注意front指向的是头数据的前一个位置,使用在读取头数据的时候的指针为front+1
}
数组模拟队列的完整实现如下:
public class ArrayQueue {
private int maxSize; //队列的最大容量
private int[] arr; //用于存放数据,模拟队列
private int front; //队列头,实际上指向队列头数据的前一个数据
private int rear; //队列尾,实际上指向队列尾的数据
//初始化队列通过构造方法完成
public ArrayQueue(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
front = -1;
rear = -1;
}
//判断队列是否为空的方法
public boolean isEmpty() {
return rear == front;
}
//判断队列是否已满的方法
public boolean isFull() {
return rear == maxSize - 1;
}
//向队列里添加数据的方法
public void addQueue(int n) {
//先判断队列是否已满
if (isFull()) {
System.out.println("队列已满,不能加入数据");
return;
}
arr[++rear] = n;
}
//取出队列数据的方法
public int getQueue() {
//先判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列已空,不能取数据");
}
return arr[++front];
}
//查看头数据的方法
public int headQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列已空,没有数据");
}
return arr[front + 1];
}
//辅助方法,显示队列所有数据
public void list() {
//判断队列是否为空
if (isEmpty()) {
System.out.println("队列为空,没有数据");
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
}
顺序队列存在一个问题:但重队列中取出数据后,已经使用过的数组元素不能在使用,导致顺序队列不可复用。也可以在每次取出元素后把所有的数据向前移,但这样做带来的开销太大,循环队列是一种好的解决方案。
循环队列
分析如下:
- 循环队列也有两个指针:
front
和rear
,与顺序队列不同的是我们将二者的值初始化为0,也就是说front
指向的是头数据,而rear
指向的尾数据的下一个数据。 - 循环队列为空的条件依然是:
rear==front
public boolean isEmpty() {
return rear == front;
}
- 循环队列已满的条件与顺序队列不同,其为:
(rear+1)%maxSize=front
。约定:数组中总是有一个位置没有数据,这个位置是当链表已满时rear
指向的位置
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
- 队列中有效数据个数为:
(real+maxSize-front)%maxSize
public int size() {
return (real + maxSize -front) % maxSize;
}
完整的代码实现如下
public class CircleArrayQueue {
private int maxSize;
private int[] arr;
private int front;
private int rear;
public CircleArrayQueue(int maxSize) {
this.maxSize = maxSize;
arr = new int[maxSize];
}
public boolean isEmpty() {
return rear == front;
}
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
public void addQueue(int n) {
if (isFull()) {
System.out.println("链表已满,不能加入数据");
}
arr[rear] = n;
rear = (rear + 1) % maxSize;
}
public int getQueue() {
if (isEmpty()) {
throw new RuntimeException("队列已空,不能取数据");
}
int value = arr[front];
front = (front + 1) % maxSize;
return value;
}
public int headQueue() {
if (isEmpty()) {
throw new RuntimeException("队列已空,没有数据");
}
return arr[front];
}
public int size() {
return (rear + maxSize - front) % maxSize;
}
public void list() {
if (isEmpty()) {
System.out.println("队列已空,没有数据")
return;
}
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
}
}
}