队列的介绍
- 队列是一个有序列表,可以用数组或是链表来实现。
- 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
- 示意图:(使用数组模拟队列示意图)
数组模拟队列思路
- 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队 列的最大容量。
- 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标, front会随着数据输出而改变,而 rear 则是随着数据输入而改变,如图所示:
- 当我们将数据存入队列时称为”addQueue”,addQueue 的处理需要有两个步骤:思路分析 :
-
1) 将尾指针往后移:rear+1 ,当 front == rear 【空】
-
2) 若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear所指的数组元素中,否则无法存入数据。 rear == maxSize - 1[队列满]
编码实现(使用数组模拟队列)
package com.zlx.queue;
import java.util.Scanner;
/**
* @ClassName ArrayQueue
* @Description TODO
* @Author zhalongxi
* @Date 2021/5/9 19:23
* @Version 1.0
**/
public class ArrayQueueDemo {
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(3);//创建出一个队列
char key = ' ';//接收用户输入的数据
Scanner scanner = new Scanner(System.in); //控制台输入数据
//在while循环中进行队列的操作
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':
try {
arrayQueue.showArrQueue(); //打印出队列中的所有的数据
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'a':
System.out.println("请输入需要往队列中添加的元素:");
int value = scanner.nextInt(); //接收控制台输入的一个整形数据
arrayQueue.addQueue(value);
break;
case 'g':
try {
arrayQueue.getQueue();//从队列中取出一个元素
} catch (Exception e) {
//处理从队列中取元素时的空队列跑出的异常
System.out.println(e.getMessage());
}
break;
case 'h':
try {
arrayQueue.getHeadQueue();//取出队列中的头元素
} catch (Exception e) {
//处理从队列中取元素时的空队列跑出的异常
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();//关闭控制台
loop = false;//结束循环
break;
}
}
System.out.println("程序退出");
}
}
class ArrayQueue{
private int maxSize;//队列的最大的容量
private int front;//队列头部
private int rear;//队列尾部
private int[] arr;//模拟队列的数组
//1 使用构造器构造出一个队列
/** @Description //TODO
* @Author zhalongxi
* @Date 19:24 2021/5/9
* @Param maxSize:队列的最大的容量
* @return
**/
public ArrayQueue(int arrMaxSize){
maxSize = arrMaxSize; //队列的最大的容量
arr = new int[maxSize]; //创建出一个队列
front = -1;//队列的头指向队列的第一个元素的前一个位置
rear = -1;//队列的尾部指向队列的第一个元素的前一个位置
}
/** @Description 判断队列是否已满
* @Author zhalongxi
* @Date 19:33 2021/5/9
* @Param
* @return
**/
public boolean isFull(){
return rear == maxSize - 1;
}
/** @Description 判断队列是否为空
* @Author zhalongxi
* @Date 19:34 2021/5/9
* @Param
* @return
**/
public boolean isEmpty(){
return rear == front;
}
/** @Description 往队列中添加数据
* @Author zhalongxi
* @Date 19:35 2021/5/9
* @Param
* @return
**/
public void addQueue(int n){
//判断队列是否已满
if(isFull()){
System.out.println("队列已满,请勿添加数据...");
return ;
}
rear ++;
arr[rear] = n;
}
/** @Description 从队列中取出一个元素
* @Author zhalongxi
* @Date 19:39 2021/5/9
* @Param
* @return
**/
public int getQueue(){
//判断队列是否为空
if(isEmpty()){
//为空则抛出异常
throw new RuntimeException("队列为空,无法从队列中获取到数据...");
}
front ++; //将队列的头部向前移动一位
System.out.println("从队列中取出的元素:"+arr[front]);
return arr[front];
}
/** @Description 打印出队列中的所有的元素
* @Author zhalongxi
* @Date 19:44 2021/5/9
* @Param
* @return
**/
public void showArrQueue(){
if(isEmpty()){
System.out.println("队列是空的...");
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
/** @Description 取出队列的头部元素
* @Author zhalongxi
* @Date 19:45 2021/5/9
* @Param
* @return
**/
public int getHeadQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空...");
}
// front ++;
System.out.println("从队列中取出的头元素:"+arr[front]);
return arr[front + 1];
}
}
注意:这个队列有一个问题:就是一次性的队列,用完就没了。。所以需要将队列进行如下的改进:将这个数组使用算法,改进成一个环形的队列 取模:%,达到复用的效果。
数组模拟环形队列
对前面的数组模拟队列的优化,充分利用数组. 因此将数组看做是一个环形的。(通过取模的方式来实现即可)
- 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的 时候需要注意 (rear + 1) % maxSize == front 满]
- rear == front [空]
- 分析示意图
编码实现
package com.zlx.queue;
import java.util.Scanner;
/**
* @ClassName CircleArrayQueueDemo
* @Description TODO
* @Author zhalongxi
* @Date 2021/5/9 22:45
* @Version 1.0
**/
public class CircleArrayQueueDemo {
public static void main(String[] args) {
//注意,此环形队列的有效数据实际上只有3,因为预留了一位用作约定
CircleArrayQueue circleArrayQueue = new CircleArrayQueue(4);//创建出一个队列
char key = ' ';//接收用户输入的数据
Scanner scanner = new Scanner(System.in);//控制台输入数据
//在while循环中进行队列的操作
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':
try {
circleArrayQueue.showQueue(); //打印出队列中的所有的数据
} catch (Exception e) {
System.out.println(e.getMessage());
}
break;
case 'a':
System.out.println("请输入需要往队列中添加的元素:");
int value = scanner.nextInt(); //接收控制台输入的一个整形数据
circleArrayQueue.addQueue(value);
break;
case 'g':
try {
circleArrayQueue.getQueue();//从队列中取出一个元素
} catch (Exception e) {
//处理从队列中取元素时的空队列跑出的异常
System.out.println(e.getMessage());
}
break;
case 'h':
try {
circleArrayQueue.getHeadQueue();//取出队列中的头元素
} catch (Exception e) {
//处理从队列中取元素时的空队列跑出的异常
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();//关闭控制台
loop = false;//结束循环
break;
default:
System.out.println("请输入上面提示的字母!");
break;
}
}
System.out.println("程序退出");
}
}
class CircleArrayQueue{
private int maxSize; //队列的容量
//front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
//front 的初始值 = 0
private int front; //指向队列的头指针(队列的第一个元素)
//rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
// rear 的初始值 = 0
private int rear; //指向队列的最后一个元素的后一个位置
private int[] arr; //用于模拟队列存储数据
/** @Description 初始化队列
* @Author zhalongxi
* @Date 22:50 2021/5/9
* @Param
* @return
**/
public CircleArrayQueue(int circleArrayMaxSize){
maxSize = circleArrayMaxSize;
arr = new int[maxSize];
//这两行代码可以不写,因为默认值为0
front = 0;
rear = 0;
}
/** @Description 判断队列是否已满,因为是环形队列,所以考虑取模
* @Author zhalongxi
* @Date 22:51 2021/5/9
* @Param
* @return
**/
public boolean isFull(){
return (rear + 1) % maxSize == front;
}
/** @Description 判断队列是否为空
* @Author zhalongxi
* @Date 22:52 2021/5/9
* @Param
* @return
**/
public boolean isEmpty(){
return rear == front;
}
/** @Description 往数组中添加元素
* @Author zhalongxi
* @Date 22:55 2021/5/9
* @Param int n添加的值
* @return
**/
public void addQueue(int n){
if(isFull()){
System.out.println("队列已满,请勿再添加...");
return;
}
//因为这个rear指正指向队列的后一个元素,所以不需要rear+1
arr[rear] = n;
//将rear后移,必须考虑到取模,否则会数组越界异常(因为是环形队列)
rear = (rear + 1) % maxSize;
}
/** @Description 取出队列中的值
* @Author zhalongxi
* @Date 22:58 2021/5/9
* @Param
* @return
**/
public int getQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,无法取出元素...");
}
//1、先将front对应的值保留到一个临时变量中
int value = arr[front];
//2、将front后移,考虑取模,否则出现数组越界异常
front = (front + 1) % maxSize;
//3、将保存的临时变量进行返回
return value;
}
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]);
}
}
/** @Description 显示队列的头元素
* @Author zhalongxi
* @Date 23:03 2021/5/9
* @Param
* @return
**/
public int getHeadQueue(){
System.out.println("队列的头元素为:"+arr[front]);
return arr[front];
}
/** @Description 计算队列中有效数据的个数
* @Author zhalongxi
* @Date 23:01 2021/5/9
* @Param
* @return
**/
public int size(){
return (rear + maxSize -front) % maxSize;
}
}