队列
- 队列是一个有序列表,可以用数组或者链表来实现
- 遵循先入先出原则。即先存入队列的数据,要先取出。后存入的要后限出
数组模拟队列
队列本身是有序表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front和rear分别记录队列的前后端的下标,front会随着数据输出而改变,而rear则是随着数据输入而改变。如图所示
当我们将数据存入队列时称为“addQueue”,addQueue的处理需要有两个步骤
- 将尾指针往后移:rear+1,当front == rear时,队列为空。
- 当尾指针rear小于队列最大下标maxSize-1,则将数据存入rear所指的数组元素中,否则无法存入数据 。rear == maxSize - 1时,队列满。
代码实现
/**
* 数组模拟队列
* 单次队列,不能重复使用
* @param <E>
*/
public class QueueArr<E> {
private int front;//前端标记
private int rear; //后端标记
private int maxSize;
private Object[] queueArr;
public QueueArr(int maxSize) {
this.maxSize = maxSize;
queueArr = new Object[maxSize];
//标记都初始化为-1
front = -1; //指向队列头的前一个位置
rear = -1; //指向队列尾元素的具体下标
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
QueueArr<Integer> queue = new QueueArr<>(10);
for (int i = 0; i < 10; i++) {
queue.add(i);
}
queue.showQueue();
System.out.println(queue.getHead());
}
/**
* 判断队列是否已满
*/
public boolean isFull() {
return maxSize - 1 == rear;
}
/**
* 判断队列是否为空
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 入队,添加元素
*/
public void add(E e) {
if (isFull()) {
throw new RuntimeException("Queue is full!");
} else {
rear++;
queueArr[rear] = e;
}
}
/**
* 出队,获取队列元素
*/
public Object get() {
if (isEmpty()) {
throw new RuntimeException("Queue is empty!");
} else {
front++;
return queueArr[front];
}
}
/**
* 遍历队列
*/
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,无法遍历");
return;
} else {
for (int i = 0; i < queueArr.length; i++) {
System.out.printf("queueArr[%d] = %s\n", i, queueArr[i]);
}
}
}
public Object getHead() {
if (isEmpty()) {
throw new RuntimeException("队列为空!");
} else {
return queueArr[front + 1];
}
}
}
1. 存在问题,队列只能使用一次,不能重复存储数据。无法复用。
2. 将这个数组使用算法,改进成一个环形的队列。取模
数组模拟环形队列
思路
-
front变量的含义调整为:front指向队列的第一个元素,也就是说arr[front]就是队列的第一个元素。front的初始值为0。
-
rear变量的含义调整为:rear指向队列的最后一个元素的后一个位置。因为希望空出一个空间作为约定。rear的初始值为0;
-
当队列满时,条件是(rear + 1) % maxSize == front时队列为满
-
当队列为空的条件,rear == front为空。
-
当我们这样分析,队列中有效的数据个数:(rear + maxSize - front) % maxSize
代码实现
/**
* 数组模拟环形队列
* 环形队列,可以重复使用
* 使用取模算法 有一个位置为约定空间,不作存储
*
* @param <E>
*/
public class CircularQueue<E> {
private int front;//前端标记,指向队列头的位置
private int rear; //后端标记,指向队列尾元素的下一个位置
private int maxSize;
private Object[] queueArr;
public CircularQueue(int maxSize) {
this.maxSize = maxSize;
queueArr = new Object[maxSize];
}
/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
System.out.println("~~~测试数组模拟环形队列~~~");
//创建一个环形队列 长度10。
CircularQueue<Integer> queue = new CircularQueue<>(10);
char key = ' '; //接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("s(show): 显示队列");
System.out.println("a(add): 添加元素");
System.out.println("g(get): 取出元素");
System.out.println("h(head): 显示队列头");
System.out.println("e(exit): 退出");
key = scanner.next().charAt(0); //接收一个字符
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
try {
System.out.println("请输入一个整数");
queue.add(scanner.nextInt());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'g':
int value = 0;
try {
value = (Integer) queue.get();
System.out.printf("取出的数据是%d\n", value);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int head = (Integer) queue.getHead();
System.out.printf("取出的数据是%d\n", head);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出!");
}
/**
* 判断队列是否已满
* 条件:(rear + 1) % maxSize == front
*/
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
/**
* 判断队列是否为空
* 条件rear == front
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 入队,添加元素
*/
public void add(E e) {
if (isFull()) {
throw new RuntimeException("Queue is full!");
} else {
queueArr[rear % maxSize] = e;
//rear后移,这里必须取模
rear = (rear + 1) % maxSize;
}
}
/**
* 出队,获取队列元素
*/
public Object get() {
if (isEmpty()) {
throw new RuntimeException("Queue is empty!");
} else {
//1.先保存front指向的元素
Object temp = queueArr[front];
//2.将front后移,要取模,否则数组越界
front = (front + 1) % maxSize;
//3.返回元素
return temp;
}
}
/**
* 有效数据的数量
*
* @return
*/
public int size() {
return (rear + maxSize - front) % maxSize;
}
/**
* 遍历队列
*/
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,无法遍历");
return;
} else {
//思路:从front开始遍历,遍历长度等于有效数量
for (int i = front; i < front + size(); i++) {
System.out.printf("queueArr[%d] = %s\n", i, queueArr[(i) % maxSize]);
}
}
}
/**
* 显示头元素
*
* @return
*/
public Object getHead() {
if (isEmpty()) {
throw new RuntimeException("队列为空!");
} else {
return queueArr[front];
}
}
}