一: 什么是队列?
1):队列是一个有序列表,可以用数组或者是链表来实现.
2):队列遵循先进先出的原则,即:先存入队列的数据,要先取出,后存入队列的数据要后取出.
如图所示:
二.用数组模拟队列的思路
1.队列本身就是一个有序的列表如果说是使用数组的结构来存储队列的数据,那么如上图所示,其中的maxSize表示为该队列的最大容量
2.由于用数组表示队列,那么队列的输出输入都是从前后端来实现的,所以我们创建了两个变量,front和rear,front表示这个模拟队列的前后端的下标,其中front用来表示输出数据,rear表示输入数据,
3.当我们向队列中添加数据时,我们需要考虑两点:
(1).需要判断队列数据是否满了,也就是尾指针rear=maxSize-1,这个时候表示数据已经满了,
(2). 当rear=front=-1时,说明该队列为空列,添加数据时需要将rear+1
(3).当我们获取队列中的数据时,我们需要考虑队列中的数据是否为空
代码展示:
package com.qiu.queue;
import java.util.Scanner;
public class ArrayQueueDemo {
public static void main(String[] args) {
//测试一下
ArrayQueue arrayQueue = new ArrayQueue(3);
char key = ' ';//接受用户输入
Scanner scanner = new Scanner(System.in);
boolean flag =true;
while (flag){
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();
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();//不退出就会被警告
flag=false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
//编写一个类,使用数组模拟队列-编写一个ArrayQueue类
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++;//让rear后移
arr[rear] = n;
}
//获取队列数据时,也就是出队列
public int getQueue(){
//判断队列是否为空
if (isEmpty()){
//通过抛出异常来处理
throw new RuntimeException("队列空,不能取数据");
}
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];
}
}
运行结果
在这里看起来代码没有什么问题,但是如果说我们在添加满了这个队列之后,然后取出了所有的数据,然后再去添加数据的时候,会提示队列中的数据已经满了
分析原因:我们在添加数据的时候,rear++了当rear=maxSize-1时,这个队列的值,已经满了,取出数据时,rear并没有发生变化.还是rear=maxSize-1.所以导致提醒数据满.
问题总结:
1.目前数组使用一次之后就不能用了,未达到重复使用的效果
2.将这个数据的算法,改进成一个环形的队列,取模的方式来完成
三:数组模拟环形队列
在这里我们需要对前面的数组队列进行优化.充分利用数组,将数组看成是一个环形的{通过取模的方式来实现}
分析说明
1.尾索引的下一个为头索引时,表示队列为满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意(rear+1)%maxSize ==front时,表示该队列已经满了
2.rear = =front时表示队列为空
3.示意图
代码演示
package com.qiu.queue;
import java.util.Scanner;
public class CircleArrayQueue {
public static void main(String[] args) {
//测试一下
System.out.println("测试环形数组===========");
CirCleArray arrayQueue = new CirCleArray(4);
char key = ' ';//接受用户输入
Scanner scanner = new Scanner(System.in);
boolean flag =true;
while (flag){
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();
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();//不退出就会被警告
flag=false;
break;
default:
break;
}
}
System.out.println("程序退出");
}
}
class CirCleArray{
private int maxSize;//最大容量
//front的含义做一个调整,front指向队列中的第一个元素,也就是说arr[front]就是第一个元素
//初始值为0
private int front;
//rear的含义做一个调整,rear指向队列中的倒数第二个元素,也就是说arr[rear]就是倒数第二个元素
//初始值为0
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;//第一次添加数据的时候这个rea为0,也就是加的第一个值时arr[0]
rear = (rear+1)%maxSize;//这里必须使用取模运算,防止越界
}
//获取队列数据时,也就是出队列
public int getQueue(){
//判断队列是否为空
if (isEmpty()){
//通过抛出异常来处理
throw new RuntimeException("队列空,不能取数据");
}
//1.这里的front就是指向队列的第一个元素,先把front对应的值保存到一个临时变量
//2.将front后移,考虑取模
//3.将临时变量返回
int value = arr[front];
front = (front+1)%maxSize;
return value;
}
//显示队列的所有数数据
public void showQueue(){
//遍历,第一步先做判断,这个队列是否为空
if (isEmpty()){
System.out.println("这个队列里面没有数据哟");
return;
}
//从front开始遍历,遍历多少个元素
for (int i = front; i <front+size() ; i++) {
System.out.printf("arr[%d] =%d\n",i%maxSize,arr[i%maxSize]);
}
}
//求出当前队列的有效个数
public int size(){
return (rear+maxSize-front)%maxSize;
}
//显示队列的头数剧,注意不是取出数据
public int headQueue(){
//判断
if (isEmpty()){
throw new RuntimeException("队列为空,没有数据!");
}
return arr[front];
}
}
结果演示: