该文是观看尚硅谷韩老师视频学习自己总结学习得,有的是来源于网络收集
进的一端称为队尾(rear),出的一端称为队头(front)。队列可以用顺序存储,也可以用链式存储。
队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的 如上图,其中 maxSize 是该队列的最大容量。
思路分析
添加数据的时候将尾指针往后移:rear+1 , 当 取出的数据front+1,若front == rear 【所明队列数据为空,并非数组数据空,】
若尾指针 rear 小于队列的最大下标 maxSize-1,则将数据存入 rear 所指的数组元素中,否则无法存入数据。
rear = = maxSize - 1[队列满]
package com.fs.demo_2020_07_11_queueArray;
import java.util.Scanner;
/**
* 队列
* 队列是一个有序列表,可以用数组或者链表来实现
* 遵循先入先出的原则,先存入的数据先取出来,后存入的后取出来
*
* 数组模拟队列
*/
public class QueueArray {
public static void main(String[] args) {
//测试一把
//创建一个队列
SimulationQueueArray queue = new SimulationQueueArray(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());
e.printStackTrace();
}
break;
case 'e': //退出
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出~~");
}
}
//创建一个类来模拟队列
class SimulationQueueArray {
private int maxSize;//表示数字的最大容纳量
private int front;//模拟指针,默认指向数组有效数据索引的前一个位置
private int rear;//模拟指针指向尾部数据,默认初始化指向和front一个位置,因为没有数据
private int[] arr;
/**
* 创建队列数组的构造器
*
* @param arrMaxSize 初始化这个队列数组的容量
*/
public SimulationQueueArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];//初始化数组的容量
front = -1;
rear = -1;
}
//判断队列数组是否满了
public boolean isFull() {
//假设数组最大索引为2,那么容量就为3,当添加3个数据后,rear就加了3次,就是-1+3=2
//所以-1+3 = 2 = 3 -1 = 2,所以就满了
return rear == maxSize - 1;
}
//判断队列是否为空
public boolean isEmpty() {
//为空有两中情况,
//1.就是数组中的元素取完了,我还是假设容量为3,要么一条数据都没有添加,front和rear都为-1,表示容量为空
//2.数组中的元素添加3个,那么front为2,然后我们又取了3个元素,那么front也为2,那么rear=front,说明数组中为空
return rear == front;
}
//添加数据到队列
public void addQueue(int data) {
//添加前先判断队列是否满了
if (isFull()) {
System.out.println("队列满,不能加入数据~~~");
return;
}
rear++;//让指针向后移一下
//在rear索引下添加这个数据,假设第一次添加,就是0索引添加data数据
arr[rear] = data;
}
//获取队列的数据,出队列
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.println("arr[" + i + "]=" + arr[i]);
}
}
//显示队列的头数据,不是取数剧
public int headQueue() {
if (isEmpty()) {
//为空就抛出异常,我们这里结束方法使用抛出异常方式
throw new RuntimeException("队列空,没有数据~~~");
}
//显示是front指针当前指的哪位数据,因为假如还没有从列队中取出数据,那么当前就指向索引为0的位子,故为front+1 为0
//若已经取了索引为0的数据,那么当前指的就是索引为1的位子,故front+1 为1
//若数据已经取完,那么就判断直接抛出异常不会到这一步,假设到了这一步,也会索引越界异常
return arr[front + 1];
}
}
若看不懂,将代码拷贝到idea中DEBUG看数据变化自然会懂
上面是线性的队列,不能循环复用其中的排队逻辑,当队列满后,第一个走后,后来的不能加入子第一个位置,所以没有达到复用效果
使用一些取模的方式达到让线性的队列转换成环形队列,从而实现先进先出,后进后出,循环环形队列
上述到达尾部又向前存储的队列称为循环队列,为了避免"假溢出",我们通常采用循环队列。
一 尾索引的下一个为头索引时表示队列满,即将队列容量空出一个作为约定,这个在做判断队列满的时候需要注意 (rear + 1) % maxSize == front 满]
package com.fs.demo_2020_07_11_queueArray;
import java.util.Scanner;
/**
* 模拟环形队列数组
*
* 1.头尾部指针取模重新计算指针值:(rear/front + 1) % maxSize
* 2.队列空的判断逻辑:rear == front
* 3.队列满的判断逻辑:(rear + 1) % maxSize == front
* 4.队列内有效数据个数:(rear - front + maxSize) % maxSize
*
*/
public class CircleQueueArray {
public static void main(String[] args) {
//测试一把
//创建一个队列
SimulationCircleQueueArray queue = new SimulationCircleQueueArray(4);
char key = ' '; //接收用户输入
Scanner scanner = new Scanner(System.in);//
boolean loop = true;
//输出一个菜单
while (loop) {
/**
* 遍历打印下每次执完后数组中的数据
*/
int[] arr = queue.getArr();
System.out.println("-------------------------------------------------");
for (int i = 0; i < arr.length; i++) {
int i1 = arr[i];
System.out.println("arr数组索引:"+i+"位子当前的数据为:"+i1+".");
}
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): 查看队列头的数据");
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());
e.printStackTrace();
}
break;
case 'e': //退出
scanner.close();
loop = false;
break;
default:
break;
}
}
System.out.println("程序退出~~");
}
}
//创建一个类来模拟环形队列数组
class SimulationCircleQueueArray{
private int maxSize;//表示数字的最大容纳量
private int front;//模拟指针指向头部数据,对于普通的队列做了调整,现在默认指向第一个索引位子,也就是0
private int rear;//模拟指针指向尾部数据,同样也是默认指向第一个索引的位子,也就是0
private int[] arr;//数组用于存放数据,模拟队列
//提供一个get方法观察数组中数据的变化
public int[] getArr() {
return arr;
}
/**
* 创建环形队列数组的构造器
*
* @param arrMaxSize 初始化这个环形队列数组的容量
*/
public SimulationCircleQueueArray(int arrMaxSize) {
maxSize = arrMaxSize;
arr = new int[maxSize];//初始化数组的容量
front = 0;
rear = 0;
}
//判断队列数组是否满了
public boolean isFull() {
/*
判断队列是否满
环形队列,判断队列满,不仅仅在通过rear = maxSize -1 来判断
因为是环形,尾部指针会回到之前的位子,所以通过取模来重新计算指针的值(rear+1)%maxSize == front;
假设队列内部数组arr的最大容量为4
队列添加数据,rear指针需取模重新计算指针指向值:rear = (rear + 1) % maxSize,并通过(rear + 1) % maxSize == front来判断队列是否已满
1.队列添加数据10,arr[0]=10,rear重新指向1,(1+1)%4!=0,队列未满
2.队列添加数据20,arr[1]=20,rear重新指向2,(2+1)%4!=0,队列未满
3.队列添加数据30,arr[2]=30,rear重新指向3,(3+1)%4==0,队列满,不能再添加数据
此时,队列内所有数据为:arr[0]=10,arr[1]=20,arr[2]=30,队列有效数据个数:(3-0+4)%4=3
*/
return (rear+1)%maxSize == front;
}
//判断队列是否为空
public boolean isEmpty() {
/*
队列当取得指针和添加的指针一致的时候,所明去完了,没有数据
*/
return rear == front;
}
//添加数据到队列
public void addQueue(int data) {
/*
队列的添加:
一般线性队列,尾部指针直接递增
而环形队列,尾部指针可能在次指向索引为0的位子,因此不能单一的通过递增来处理,需要取模重新计算尾部的指针位置
*/
//添加前先判断队列是否满了
if (isFull()) {
System.out.println("队列满,不能加入数据~~~");
return;
}
arr[rear] = data;//在尾部指针指向的位子添加传递的数据
//添加值后,尾部指针的走向不能在++递增来实现,需要从新取模运算
rear = (rear + 1)%maxSize;
}
//获取队列的数据,出队列
public int getQueue() {
/*
环形列队取数据:
和添加数据一样,添加是尾部指针不能一直递增,需要取模重新计算尾部指针的位子
而头部指针在每次取出数据后也不能一直递增,需要取模让指针回到最初的位子
*/
//先判断队列是否为空
if (isEmpty()) {
//为空就抛出异常,我们这里结束方法使用抛出异常方式
throw new RuntimeException("队列空,不能获取数据~~~");
}
//先获取到头部指针指向的索引位子获取出数据
int value = arr[front];
//对取模计算出下一次头部指针的位子
front = (front + 1) % maxSize;
//将数据返回
return value;
}
/**
* 显示队列所有的数据
*/
public void showQueue() {
if (isEmpty()) {
System.out.println("队列为空,无法显示队列的全部数据");
return;
}
// 遍历数组
// 不能单一的对角标递增处理,会出现角标越界异常
// 应对环形队列内部的有效数据,从头部数据开始遍历依次取出
// 有效数据的下标:i % maxSize
for (int i = front; i < front + size(); i++) {
System.out.printf("arr[%d] = %d\n", i % maxSize, arr[i % maxSize]);
}
}
/**
* 获取队列有效数据的个数
* @return 队列有效数据的个数
*/
private int size() {
return (rear - front + maxSize) % maxSize;
}
//显示队列的头数据,不是取数据
public int headQueue() {
/*
获取列队头部数据
就是获取头部指针指向的位子
*/
if (isEmpty()) {
//为空就抛出异常,我们这里结束方法使用抛出异常方式
throw new RuntimeException("队列空,没有数据~~~");
}
return arr[front];
}
}
D:\Java\JDK\java1.8
-------------------------------------------------
arr数组索引:0位子当前的数据为:0.
arr数组索引:1位子当前的数据为:0.
arr数组索引:2位子当前的数据为:0.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
10
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:0.
arr数组索引:2位子当前的数据为:0.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
20
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:0.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
s
arr[0] = 10
arr[1] = 20
arr[2] = 30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
g
取出的数据是10
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
s
arr[1] = 20
arr[2] = 30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
g
取出的数据是20
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
s
arr[2] = 30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
h
队列头的数据是30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
g
取出的数据是30
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
h
java.lang.RuntimeException: 队列空,没有数据~~~
at com.fs.demo_2020_07_11_queueArray.SimulationCircleQueueArray.headQueue(CircleQueueArray.java:203)
at com.fs.demo_2020_07_11_queueArray.CircleQueueArray.main(CircleQueueArray.java:60)
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
s
队列为空,无法显示队列的全部数据
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:0.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
40
-------------------------------------------------
arr数组索引:0位子当前的数据为:10.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:40.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
50
-------------------------------------------------
arr数组索引:0位子当前的数据为:50.
arr数组索引:1位子当前的数据为:20.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:40.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
a
输出一个数
60
-------------------------------------------------
arr数组索引:0位子当前的数据为:50.
arr数组索引:1位子当前的数据为:60.
arr数组索引:2位子当前的数据为:30.
arr数组索引:3位子当前的数据为:40.
-------------------------------------------------
s(show): 显示队列
e(exit): 退出程序
a(add): 添加数据到队列
g(get): 从队列取出数据
h(head): 查看队列头的数据
下面解释来源于网络收集
作者:rainchxy
链接:https://www.jianshu.com/p/9ba8a65464dd
来源:简书
主要是为了处理临界状态,即Q.rear向后移动一个位置Q.rear+1后,很有可能超出了数组的下标,这时它的下一个位置其实是0,如果将一维数组画成环形图,如图所示:
上图中最大空间Maxsize,当Q.rear=Maxsize-1时,(Q.rear+1)%Maxsize=0,而且Q.front=0,正好满足队满的条件:(Q.rear+1) %Maxsize= Q.front,此时为队满。
因此无论是front还是rear向后移动一个位置时,都要加1与最大空间Maxsize取模运算,处理临界问题。
总结:
队空:Q.front=Q.rear; // Q.rear和Q.front指向同一个位置
队满: (Q.rear+1) %Maxsize=Q.front; // Q.rear向后移一位正好是Q.front
入队:
Q.base[Q.rear]=x; //将元素放入Q.rear所指空间,
Q.rear =( Q.rear+1) %Maxsize; // Q.rear向后移一位
出队:
e= Q.base[Q.front]; //用变量记录Q.front所指元素,
Q.front=(Q.front+1) %Maxsize // Q. front向后移一位