Java环形队列 史上最易懂
1.什么是队列
队列是一种数据结构,用于实现优先级队列和双向循环队列等算法。它由一组元素和一个队尾指针组成,其中队尾指针指向队列中最后一个元素,而队头指针指向队列中第一个元素,用于表示队列的插入和删除操作。
在队列中,元素按照升序或降序排列,可以通过队头指针和队尾指针进行访问和操作。队列的主要应用包括优先级队列、双向循环队列、堆栈等。
队列是一种线性数据结构,因此它的空间复杂度是 O(n),其中 n 是队列中元素的数量。队列的时间复杂度通常是 O(nlogn) 或 O(n),取决于使用的具体算法。
2.数组模拟队列
队列是一个有序列表,可以用数组或是链表来实现。
遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front及 rear分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear则是随着数据输入而改变,如图所示:
思路如下:
- front :就指向队列的第一个元素, front 的初始值 = 0
- rear :指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定,rear 的初始值 = 0
- 当队列满时,条件是 (rear + 1) % maxSize == front 【满】
- 对队列为空的条件, rear == front 【空】
- 队列中有效数据的个数 (rear + maxSize - front) % maxSize
其中判断队列满和求有效数据的话,可以看下图,因为是环形的,起初front=rear=0,每当添加元素时,将rear++,但是其实预留了一个约定空间,比如定义的队列数组长度为4时,但是实际上可以使用的地址就是0,1,2此时rear=3, 3这个约定空间用来判断队满的条件**(rear+1)%maxSize == (3+1)%4 == 0 == front**(大家可以带入数值试试)。
队列的有效数据:
当rear在front之后:有效数据个数=rear-front=(rear+maxSize-front)%maxSize(大家同样可以带入数值试试)
当rear在front之前:有效数据个数=(rear+maxSize-front)%maxSize
3.Java代码实现
3.1环形队列类
public static class ArrayQueue {
private int maxSize; //表示数组最大容量
private int front; //队列头部
private int rear;//队列尾
private int[] arr; //模拟队列的数组
public ArrayQueue(int maxSize) {
front = 0; //指向队列元素第一位
rear = 0; //指向队列最后一个元素的后一位
this.maxSize = maxSize;//赋值队列的最大容量
arr = new int[maxSize];//初始化模拟队列的数组
}
//判断队列是否为满
public boolean isFull() {
return (rear + 1) % maxSize == front; //这里环形数组会预留一个位置
// 所以maxsize为3的时候 只会存入2个数值
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front; //如果头指针等于尾指针则队列为空
}
//添加数据到队列
public void addQueue(int num) {
// 判断队列是否满
if (isFull()) {
System.out.println("队列满,不能加入数据!!!");
return;
}
//直接将数据加入
arr[rear] = num;
//将 rear 后移, 这里必须考虑取模,当rear指向最后一个位置的时候,
// 加1则会重新标记为第一个位置,从而实现了环形
rear = (rear + 1) % maxSize;
}
//数据出队列
public int getQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空!");
}
int val = arr[front]; //先把front指向的数据取出来
front = (front + 1) % maxSize; //头指针后移
return val; //返回数据
}
//显示队列内容
public void showQueue() {
for (int i = front; i < front + size(); i++) {
System.out.println(arr[i]);
}
}
public int size() {
return (rear + maxSize - front) % maxSize;//其实也是rear-front演化过来
//小伙伴可以好好思考一下
}
//查看队列第一个元素
public int headQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空!");
}
return arr[front]; //返回数据
}
}
3.2代码测试类
import java.util.Scanner;
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(5); //初始化一个队列
char key = ' '; //用于判断用户的操作
Scanner scanner = new Scanner(System.in);
boolean flag = true;
while (flag) {
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':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("请输入一个数字");
int val = scanner.nextInt();
try {
arrayQueue.addQueue(val); //如果队列中没有数据则会报错,所以用了个try,catch防止报错
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'g':
try {
System.out.println("取出的数据是:" + arrayQueue.getQueue());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try { //如果队列中没有数据则会报错,所以用了个try,catch防止报错
System.out.println("队列头数据是:" + arrayQueue.headQueue());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
flag = false;
scanner.close();
break;
}
}
System.out.println("退出程序成功");
}
3.3完整Java代码
import java.util.Scanner;
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(5); //初始化一个队列
char key = ' '; //用于判断用户的操作
Scanner scanner = new Scanner(System.in);
boolean flag = true;
while (flag) {
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':
arrayQueue.showQueue();
break;
case 'a':
System.out.println("请输入一个数字");
int val = scanner.nextInt();
try {
arrayQueue.addQueue(val); //如果队列中没有数据则会报错,所以用了个try,catch防止报错
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'g':
try {
System.out.println("取出的数据是:" + arrayQueue.getQueue());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'h':
try { //如果队列中没有数据则会报错,所以用了个try,catch防止报错
System.out.println("队列头数据是:" + arrayQueue.headQueue());
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'e':
flag = false;
scanner.close();
break;
}
}
System.out.println("退出程序成功");
}
public static class ArrayQueue {
private int maxSize; //表示数组最大容量
private int front; //队列头部
private int rear;//队列尾
private int[] arr; //模拟队列的数组
public ArrayQueue(int maxSize) {
front = 0; //指向队列元素第一位
rear = 0; //指向队列最后一个元素的后一位
this.maxSize = maxSize;//赋值队列的最大容量
arr = new int[maxSize];//初始化模拟队列的数组
}
//判断队列是否为满
public boolean isFull() {
return (rear + 1) % maxSize == front; //这里环形数组会预留一个位置
// 所以maxsize为3的时候 只会存入2个数值
}
//判断队列是否为空
public boolean isEmpty() {
return rear == front; //如果头指针等于尾指针则队列为空
}
//添加数据到队列
public void addQueue(int num) {
// 判断队列是否满
if (isFull()) {
System.out.println("队列满,不能加入数据!!!");
return;
}
//直接将数据加入
arr[rear] = num;
//将 rear 后移, 这里必须考虑取模,当rear指向最后一个位置的时候,
// 加1则会重新标记为第一个位置,从而实现了环形
rear = (rear + 1) % maxSize;
}
//数据出队列
public int getQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空!");
}
int val = arr[front]; //先把front指向的数据取出来
front = (front + 1) % maxSize; //头指针后移
return val; //返回数据
}
//显示队列内容
public void showQueue() {
for (int i = front; i < front + size(); i++) {
System.out.println(arr[i]);
}
}
public int size() {
return (rear + maxSize - front) % maxSize;//其实也是rear-front演化过来
//小伙伴可以好好思考一下
}
//查看队列第一个元素
public int headQueue() {
//判断队列是否为空
if (isEmpty()) {
throw new RuntimeException("队列为空!");
}
return arr[front]; //返回数据
}
}
}
3.4输出结果
D:\javazl\javaWeb\jdk1.8\bin\java.exe "-javaagent:D:\develop\idea\IntelliJ IDEA 2021.2.1\lib\idea_rt.jar=64844:D:\develop\idea\IntelliJ IDEA 2021.2.1\bin" -Dfile.encoding=UTF-8 -classpath D:\javazl\javaWeb\jdk1.8\jre\lib\charsets.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\access-bridge-64.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\cldrdata.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\dnsns.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\jaccess.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\jfxrt.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\localedata.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\nashorn.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\sunec.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\sunjce_provider.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\sunmscapi.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\sunpkcs11.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\ext\zipfs.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\jce.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\jfr.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\jfxswt.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\jsse.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\management-agent.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\resources.jar;D:\javazl\javaWeb\jdk1.8\jre\lib\rt.jar;E:\java\DateStructures\out\production\DateStructures com.itcy.queue.ArrayQueueDemo
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
a
请输入一个数字
1
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
a
请输入一个数字
2
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
s
1
2
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
g
取出的数据是:1
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
s
2
s(show),显示队列
a(add),添加数据到队列
g(get),从队列取出数据
h(head),查看对头数据
e(exit),退出程序
e
退出程序成功
Process finished with exit code 0