1、队列的一个使用场景
银行排队的案例:
2、队列介绍
- 队列是一个 有序列表,可以用 数组或是 链表来实现。
- 遵循 先入先出的原则。即: 先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图)
3、数组模拟队列思路
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队 列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标, front
会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:
当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析
- 将尾指针往后移:rear+1 , 当 front == rear 【空】
- 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。
rear == maxSize - 1[队列满]
代码实现
ArrayQueue.java
package com.yg.datastructures.queue;
/**
* @Description: 数组 模拟 队列 模型
* @Author yg
* @Date 2021-03-20 10:33
*/
public class ArrayQueue {
private final int maxSize;
/**
* 该数组存放数据,模拟队列
*/
private final String[] arr;
/**
* 队列头 指针
*/
private final int front;
/**
* 队列尾部 指针
*/
private int rear;
/**
* 串讲队列 构造器
*/
public ArrayQueue(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new String[maxSize];
/**
* 指向队列头部,
* 指向队列头部的前一个位置。
*
*/
front = -1;
/**
* 指向队列尾
* 指向队列尾的数据,指向队列的最后一个数据
*/
rear = -1;
}
/**
* 判断队列是否满了
*
* @return
*/
public boolean isFull() {
return rear == maxSize - 1;
}
/**
* 判断队列是否为空
*
* @return
*/
public boolean isEmpty() {
return rear == front;
}
/**
* 添加数据到队列
*/
public void addQueue(String data) {
if (isFull()) {
System.out.println("队列已经满了,不能加入数据");
return;
}
arr[++rear] = data;
}
/**
* 获取队列的一个元素
*
* @return String 数据
*/
public String getQueue() {
if (isEmpty()) {
System.out.println("队列已经没有数据了,对不起");
throw new RuntimeException("队列已经没有数据了,不能获取数据");
}
//front 指向的是队列头的钱一个位置
return arr[++front];
}
/**
* 显示队列的所有数据
*/
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,没有数据!");
return;
}
System.out.println("-------------------开始打印队列-------------------");
for (String data : arr) {
System.out.println(data);
}
System.out.println("-------------------结束打印队列-------------------");
}
/**
* 显示队列的头部数据
*/
public void showHeadQueue() {
if (isEmpty()) {
throw new RuntimeException("队列为空,没有头部数据显示!");
}
System.out.println("队列的头部元素是:" + arr[front + 1]);
}
}
ArrayQueueTest.java
package com.yg.datastructures.queue;
import java.util.Scanner;
/**
* @Description: 使用数组模拟队列
* @Author yg
* @Date 2021-03-20 10:31
*/
public class ArrayQueueTest {
public static void main(String[] args) {
System.out.println("----------------程序开始----------------------");
ArrayQueue arrayQueue = new ArrayQueue(5);
// arrayQueue.addQueue("yg");
// arrayQueue.addQueue("fm");
// arrayQueue.addQueue("lb");
// arrayQueue.addQueue("was");
// arrayQueue.addQueue("bjy");
// arrayQueue.showQueue();
// arrayQueue.showHeadQueue();
// String queue = arrayQueue.getQueue();
// System.out.println("获取到队列的一个元素 = " + queue);
// arrayQueue.showQueue();
// arrayQueue.showHeadQueue();
//
Scanner scanner = new Scanner(System.in);
boolean loop = true;
while (loop) {
System.out.println("---------------控制台----------------");
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) : 展示队列头部元素");
System.out.println("---------------控制台----------------");
String input = scanner.next();
switch (input) {
case "s":
arrayQueue.showQueue();
break;
case "e":
scanner.close();
loop = false;
break;
case "a":
System.out.println("请输入添加的元素:");
String next = scanner.next();
arrayQueue.addQueue(next);
break;
case "h":
try {
arrayQueue.showHeadQueue();
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case "g":
try {
String queue = arrayQueue.getQueue();
System.out.println("头部元素 =" + queue);
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
default:
break;
}
}
System.out.println("----------------程序结束----------------------");
}
}
问题分析并优化
- 目前数组使用一次就不能用, 没有达到复用的效果
- 将这个数组使用算法,改进成一个列 环形的队列 取模:%
解决方法1:
改造获取头部元素的方法,当获取了一个元素之后,将其他元素统一向前移动。
/**
* 获取队列的一个元素
*
* @return String 数据
*/
public String getQueue() {
if (isEmpty()) {
System.out.println("队列已经没有数据了,对不起");
throw new RuntimeException("队列已经没有数据了,不能获取数据");
}
//front 指向的是队列头的钱一个位置
//front++;
String head = arr[front + 1];
//这里删除第一个元素,并把其他的元素向前移动 ,front 不需要移动。
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
//尾部指针 -1
rear--;
return head;
}
该方法满足数组的重复利用,但是存在一个效率问题,当队列特别大的时候,需要统一遍历一遍队列并移动,并且每次获取一个元素的时候都需要该遍历移动。
改造后运行结果:
D:\jdk1.8.0_251\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:14148,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2020.3\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\jdk1.8.0_251\jre\lib\charsets.jar;D:\jdk1.8.0_251\jre\lib\deploy.jar;D:\jdk1.8.0_251\jre\lib\ext\access-bridge-64.jar;D:\jdk1.8.0_251\jre\lib\ext\cldrdata.jar;D:\jdk1.8.0_251\jre\lib\ext\dnsns.jar;D:\jdk1.8.0_251\jre\lib\ext\jaccess.jar;D:\jdk1.8.0_251\jre\lib\ext\jfxrt.jar;D:\jdk1.8.0_251\jre\lib\ext\localedata.jar;D:\jdk1.8.0_251\jre\lib\ext\nashorn.jar;D:\jdk1.8.0_251\jre\lib\ext\sunec.jar;D:\jdk1.8.0_251\jre\lib\ext\sunjce_provider.jar;D:\jdk1.8.0_251\jre\lib\ext\sunmscapi.jar;D:\jdk1.8.0_251\jre\lib\ext\sunpkcs11.jar;D:\jdk1.8.0_251\jre\lib\ext\zipfs.jar;D:\jdk1.8.0_251\jre\lib\javaws.jar;D:\jdk1.8.0_251\jre\lib\jce.jar;D:\jdk1.8.0_251\jre\lib\jfr.jar;D:\jdk1.8.0_251\jre\lib\jfxswt.jar;D:\jdk1.8.0_251\jre\lib\jsse.jar;D:\jdk1.8.0_251\jre\lib\management-agent.jar;D:\jdk1.8.0_251\jre\lib\plugin.jar;D:\jdk1.8.0_251\jre\lib\resources.jar;D:\jdk1.8.0_251\jre\lib\rt.jar;D:\IdeaProjects\datastructures\target\classes;D:\RepMaven\org\springframework\boot\spring-boot-starter-web\2.4.4\spring-boot-starter-web-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter\2.4.4\spring-boot-starter-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot\2.4.4\spring-boot-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-autoconfigure\2.4.4\spring-boot-autoconfigure-2.4.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-logging\2.4.4\spring-boot-starter-logging-2.4.4.jar;D:\RepMaven\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\RepMaven\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\RepMaven\org\apache\logging\log4j\log4j-to-slf4j\2.13.3\log4j-to-slf4j-2.13.3.jar;D:\RepMaven\org\apache\logging\log4j\log4j-api\2.13.3\log4j-api-2.13.3.jar;D:\RepMaven\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;D:\RepMaven\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;D:\RepMaven\org\yaml\snakeyaml\1.27\snakeyaml-1.27.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-json\2.4.4\spring-boot-starter-json-2.4.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-databind\2.11.4\jackson-databind-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-annotations\2.11.4\jackson-annotations-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\core\jackson-core\2.11.4\jackson-core-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.11.4\jackson-datatype-jdk8-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.11.4\jackson-datatype-jsr310-2.11.4.jar;D:\RepMaven\com\fasterxml\jackson\module\jackson-module-parameter-names\2.11.4\jackson-module-parameter-names-2.11.4.jar;D:\RepMaven\org\springframework\boot\spring-boot-starter-tomcat\2.4.4\spring-boot-starter-tomcat-2.4.4.jar;D:\RepMaven\org\apache\tomcat\embed\tomcat-embed-core\9.0.44\tomcat-embed-core-9.0.44.jar;D:\RepMaven\org\glassfish\jakarta.el\3.0.3\jakarta.el-3.0.3.jar;D:\RepMaven\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.44\tomcat-embed-websocket-9.0.44.jar;D:\RepMaven\org\springframework\spring-web\5.3.5\spring-web-5.3.5.jar;D:\RepMaven\org\springframework\spring-beans\5.3.5\spring-beans-5.3.5.jar;D:\RepMaven\org\springframework\spring-webmvc\5.3.5\spring-webmvc-5.3.5.jar;D:\RepMaven\org\springframework\spring-aop\5.3.5\spring-aop-5.3.5.jar;D:\RepMaven\org\springframework\spring-context\5.3.5\spring-context-5.3.5.jar;D:\RepMaven\org\springframework\spring-expression\5.3.5\spring-expression-5.3.5.jar;D:\RepMaven\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar;D:\RepMaven\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;D:\RepMaven\org\springframework\spring-core\5.3.5\spring-core-5.3.5.jar;D:\RepMaven\org\springframework\spring-jcl\5.3.5\spring-jcl-5.3.5.jar;D:\JetBrains\IntelliJ IDEA 2020.3\lib\idea_rt.jar" com.yg.datastructures.queue.ArrayQueueTest
Connected to the target VM, address: '127.0.0.1:14148', transport: 'socket'
----------------程序开始----------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
1111
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
2222
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
333
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
444
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
g
头部元素 =1111
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
2222
333
444
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:2222
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
g
头部元素 =2222
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
333
444
null
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:333
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
333
444
null
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
333
444
5555
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
g
头部元素 =333
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
444
5555
null
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
6666
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
777
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
444
5555
6666
777
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:444
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
g
头部元素 =444
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
5555
6666
777
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
a
请输入添加的元素:
888
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
5555
6666
777
888
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
h
队列的头部元素是:5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
g
头部元素 =5555
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
s
-------------------开始打印队列-------------------
6666
777
888
null
null
-------------------结束打印队列-------------------
---------------控制台----------------
s(show) : 展示队列
e(exit) : 退出队列
a(add) : 添加队列
g(get) : 获取队列头部元素
h(head) : 展示队列头部元素
---------------控制台----------------
e
----------------程序结束----------------------
Disconnected from the target VM, address: '127.0.0.1:14148', transport: 'socket'
Process finished with exit code 0
解决方法2:
采用环形数组模拟队列