引言
在计算机科学中,队列(Queue)是一种常见的数据结构,遵循“先进先出”(FIFO)的原则。队列的应用场景非常广泛,例如任务调度、消息队列、缓冲区管理等。本文将深入探讨如何使用数组来模拟队列,并进一步实现环形队列。通过代码示例和详细解析,帮助读者理解队列的核心概念及其实现方式。
一、数组模拟队列的实现
1.1 队列的基本概念
队列是一种线性数据结构,支持两种基本操作:
-
入队(Enqueue):将元素添加到队列的尾部。
-
出队(Dequeue):从队列的头部移除元素。
队列的特点是先进先出,即最先入队的元素最先出队。
1.2 数组模拟队列的代码实现
我们首先来看如何使用数组来模拟一个简单的队列。以下是代码实现:
public class ArrayQueueDemo {
}
// 使用数组模拟队列
class ArrayQueue {
private int maxSize; // 表示数组的最大容量
private int front; // 队列头
private int rear; // 队列尾
private int[] arr; // 存放数据,模拟队列
// 创建队列的构造器
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
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 n) {
// 先判断队列是否已满
if (isFull()) {
System.out.println("队列满,不能加入数据");
return;
}
rear++; // 让队尾向后移动
arr[rear] = n; // 赋值
}
// 获取队列的数据,出队列
public int getQueue() {
// 判断队列是否为空
if (isEmpty()) {
// 队列为空抛出异常
throw new RuntimeException("队列空,不能取数据");
}
front++; // front后移
return arr[front];
}
// 显示队列的所有数据
public void showQueue() {
// 遍历
if (isEmpty()) {
System.out.println("队列空的,没有数据");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n", i, arr[i]);
}
}
// 显示队列的头数据,注意不是取数据
public int headQueue() {
// 判断
if (isEmpty()) {
throw new RuntimeException("队列为空,没有数据");
}
return arr[front + 1];
}
}
1.3 代码解析
-
front 和 rear:分别指向队列的头部和尾部。初始时,
front
和rear
都为 -1,表示队列为空。 -
isFull():判断队列是否已满,条件是
rear == maxSize - 1
。 -
isEmpty():判断队列是否为空,条件是
rear == front
。 -
addQueue(int n):将元素添加到队列尾部。如果队列已满,则提示无法添加。
-
getQueue():从队列头部移除元素并返回。如果队列为空,则抛出异常。
-
showQueue():显示队列中的所有元素。
-
headQueue():查看队列头部的元素,但不移除。
1.4 测试代码
public static void main(String[] args) {
// 创建一个队列
ArrayQueue arrayQueue = new ArrayQueue(3);
char key = ' '; // 接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("s(show):显示队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列中获取数据");
System.out.println("h(head):查看队列头");
key = scanner.next().charAt(0); // 接收一个字符
switch (key) {
case 's':
arrayQueue.showQueue();
System.out.printf("\n");
break;
case 'a':
System.out.println("输入一个数");
int value = scanner.nextInt();
arrayQueue.addQueue(value);
break;
case 'g':
try {
int res = arrayQueue.getQueue();
System.out.printf("取出的数据是%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try {
int res = arrayQueue.headQueue();
System.out.printf("队列头的数据是%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop = false;
break;
default:
break;
}
}
}
1.5 测试结果
通过上述代码,我们可以实现一个简单的队列,并进行入队、出队、查看队列头等操作。测试结果表明,该队列能够正常工作。
二、环形队列的实现
2.1 环形队列的概念
在普通队列中,当队列满时,即使队列头部有空闲空间,也无法继续添加元素。为了解决这个问题,引入了环形队列(Circular Queue)。环形队列通过循环利用数组空间,使得队列在逻辑上形成一个环,从而更高效地利用存储空间。
2.2 环形队列的代码实现
以下是环形队列的代码实现:
public class CircleArrayQueueDemo {
}
class CircleArray {
private int maxSize; // 表示数组的最大容量
private int front; // 指向队列的第一个元素
private int rear; // 指向队列的最后一个元素的后一个位置
private int[] arr; // 该数据用于存放数据, 模拟队列
public CircleArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];
}
// 判断队列是否满
public boolean isFull() {
return (rear + 1) % maxSize == front;
}
// 判断队列是否为空
public boolean isEmpty() {
return rear == front;
}
// 添加数据到队列中
public void addQueue(int n) {
// 判断队列是否已经满
if (isFull()) {
System.out.println("队列满,不能加入数据");
return;
}
// 直接将数据加入
arr[rear] = n;
// 将rear指针后移,这里要考虑到取模
rear = (rear + 1) % maxSize;
}
// 获取队列的数据,出队列
public int getQueue() {
// 判断队列是否为空
if (isEmpty()) {
// 抛出异常
throw new RuntimeException("队列为空,不能取数据");
}
int value = arr[front];
// 和新增队列一样,同样需要取余
front = (front + 1) % maxSize;
return value;
}
// 显示队列所有数据的个数
public int size() {
return (rear + maxSize - front) % maxSize;
}
// 显示队列所有数据
public void showQueue() {
// 判断是否为空
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]);
}
}
// 显示队列的头数据,注意不是取数据
public int headQueue() {
// 判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列空的,没有数据");
}
return arr[front];
}
}
2.3 代码解析
-
front 和 rear:
front
指向队列的第一个元素,rear
指向队列的最后一个元素的后一个位置。 -
isFull():判断队列是否已满,条件是
(rear + 1) % maxSize == front
。 -
isEmpty():判断队列是否为空,条件是
rear == front
。 -
addQueue(int n):将元素添加到队列尾部。如果队列已满,则提示无法添加。
-
getQueue():从队列头部移除元素并返回。如果队列为空,则抛出异常。
-
size():计算队列中元素的个数。
-
showQueue():显示队列中的所有元素。
-
headQueue():查看队列头部的元素,但不移除。
2.4 测试代码
public static void main(String[] args) {
// 测试一把
System.out.println("测试数组模拟环形队列的案例~~~");
// 创建一个环形队列
CircleArray queue = new CircleArray(4); // 说明设置4, 其队列的有效数据最大是3
char key = ' '; // 接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
// 输出一个菜单
while (loop) {
System.out.println("s(show): 显示队列");
System.out.println("e(exit): 退出程序");
System.out.println("a(add): 添加数据到队列");
System.out.println("g(get): 从队列取出数据");
System.out.println("h(head): 查看队列头的数据");
key = scanner.next().charAt(0); // 接收一个字符
switch (key) {
case 's':
queue.showQueue();
break;
case 'a':
System.out.println("输出一个数");
int value = scanner.nextInt();
queue.addQueue(value);
break;
case 'g': // 取出数据
try {
int res = queue.getQueue();
System.out.printf("取出的数据是%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h': // 查看队列头的数据
try {
int res = queue.headQueue();
System.out.printf("队列头的数据是%d\n", res);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e': // 退出
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出~~");
}
2.5 测试结果
通过上述代码,我们可以实现一个环形队列,并进行入队、出队、查看队列头等操作。测试结果表明,该环形队列能够正常工作,并且能够循环利用数组空间。
三、总结
本文详细介绍了如何使用数组模拟队列和环形队列,并通过代码示例和测试展示了其实现过程。普通队列的实现相对简单,但在空间利用率上存在局限性。而环形队列通过循环利用数组空间,解决了普通队列的空间浪费问题,适用于需要高效利用内存的场景。
在实际开发中,队列的应用非常广泛,例如任务调度、消息队列、缓冲区管理等。理解队列的基本原理及其实现方式,对于编写高效、可靠的程序具有重要意义。希望本文能够帮助读者深入理解队列的概念及其实现方式,并在实际项目中灵活运用。