文章目录
- 1、队列(Queue)
- 1.1 队列的定义
- 2、利用数组模拟顺序队列
- 2.1、创建一个类
- 2.2、判断队列是否满或空
- 2.3、给队列添加数据
- 2.3、出队列
- 2.4、显示队列所以数据
- 2.5、显示队列的头数据(注意不是取出数据)
- 2.6、测试队列
- 3、利用数组模拟循环队列(环形队列)
- 3.1、环形队列的定义(引用大话数据结构,资源我也上传了)
- 3.2、代码实现
- 3.2.1、创建一个类
- 3.2.2、判断队列是否满或空
- 3.2.3、给队列添加数据
- 3.2.3、出队列
- 3.2.4、队列的有效值个数
- 3.2.5、显示队列所有数据
- 3.2.6、显示对队列的头数据(注意不是取出数据)
- 3.2.7、测试循环队列(解决假溢出)
1、队列(Queue)
线性表:是最基本、最简单但是却是最常用的数据结构,数据元素之间存在一对一的线性关系,有顺序存储和链式存储等存储结构。
1.1 队列的定义
队列只允许在一端进行插入操作而在另一端进行删除操作的线性表
队列是一种先进先出的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为对头。列是q=(a1a2…an)那么a1就是队头元素,而an是队尾元素。这样我们就可以删除时总是从a1开始,而插入时,列在最后,这也比较符合我们通常生活中的习惯,排在第一个的优先出列,最后来的当然排在队伍最后。
2、利用数组模拟顺序队列
数组模拟队列
队列本身是有序列表,若使用数组的结构来存储队列的数据的话,maxSize是该队列的最大容量。
因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front以及rear分别记录队列前后端的下标
存入队列思路:
1)将尾指针往后移:rear++,当front等于ear代表队列为空,不可取数据
2)rear==maxSize-1代表队列满了,不可加入数据 队列本身是有序列表,若使用数组的结构来存储队列的数据的话,maxSize是该队列的最大容量。因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front以及rear分别记录队列前后端的下标。
存入队列思路:
1)将尾指针往后移:rear++,当**front等于rear
代表队列为空,不可取数据
2)rear==maxSize-1
代表队列满了,不可加入数据
2.1、创建一个类
定义四个变量,maxSize表示数组的最大容量,front指向队列的头部,rear就是队列尾部,arr存放数据模拟队列,并赋初始值。
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; //指向队列头部,分析出front是指向队列头的前一个位置
rear=-1; // 指向队列尾部,指向队列尾部的数据
}
}
2.2、判断队列是否满或空
当队列满了的判断条件是:rear==maxSize-1
当队列为空的判断条件是:rear==front
// 判断队列是否满
public Boolean isFull(){
return rear==maxSize-1 ;
}
// 判断队列是否空
public Boolean isEmpty(){
return front==rear;
}
2.3、给队列添加数据
思路:首先判断队列是否满了,如果满了就不能添加数据,其次就是添加数据之前,rear要进行后移。(初始值为-1)
// 入队列操作
public void addQueue(int item){
// 判断队列是否满了
if(isFull()){
System.out.println("队列已满,不可加入数据");
return;
}
// 尾部后移
rear++;
arr[rear]=item;
}
2.3、出队列
思路:首先判断队列是否为空,如果为空,则不能取出数据,其次就是取出数据之前,front要进行后移。(初始值为-1)
public int getQueue(){
// 判断队列是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,不可取数据");
}
front++;
return arr[front];
}
2.4、显示队列所以数据
// 显示队列的所有数据
public void showQueue(){
// 遍历
if(isEmpty()){
throw new RuntimeException("队列为空,不可取数据");
}
for(int i=0;i<arr.length;i++){
System.out.printf("arr[%d]=%d\n",i,arr[i]);
}
}
2.5、显示队列的头数据(注意不是取出数据)
取出数据是指把数据取出之后,队列空间应该释放,如果当前队列是满,那么取出数据,队列就不是满。
public int headQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,没有头数据");
}
return arr[front+1];
}
2.6、测试队列
在主类中创建一个菜单测试
ArrayQueue arrayQueue=new ArrayQueue(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):查看队列头的数据");
System.out.println("++++++++++++++++++++++++++++++++++++");
System.out.printf("输出你想要进行的操作:");
key =scanner.next().charAt(0); // 接收一个字符
switch (key){
case 's':
try {
arrayQueue.showQueue();
}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{
int res = arrayQueue.getQueue();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h': // 查看头数据
try{
int head=arrayQueue.headQueue();
System.out.printf("队列头数据显示为%d\n",head);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop=false;
break;
default:
break;
}
System.out.println("程序退出~~");
}
全部代码如下:
/**
* @User: 老潘
* @date 2022年10月11日9:43
* 数组模拟队列(先进先出)
* 存在问题:数组不能重复使用
*/
public class arryQu {
public static void main(String[] args) {
ArrayQueue arrayQueue=new ArrayQueue(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):查看队列头的数据");
System.out.println("++++++++++++++++++++++++++++++++++++");
System.out.printf("输出你想要进行的操作:");
key =scanner.next().charAt(0); // 接收一个字符
switch (key){
case 's':
try {
arrayQueue.showQueue();
}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{
int res = arrayQueue.getQueue();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h': // 查看头数据
try{
int head=arrayQueue.headQueue();
System.out.printf("队列头数据显示为%d\n",head);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop=false;
break;
default:
break;
}
System.out.println("程序退出~~");
}
}
}
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; //指向队列头部,分析出front是指向队列头的前一个位置
rear=-1; // 指向队列尾部,指向队列尾部的数据
}
// 判断队列是否满
public Boolean isFull(){
return rear==maxSize-1 ;
}
// 判断队列是否空
public Boolean isEmpty(){
return front==rear;
}
// 入队列操作
public void addQueue(int item){
// 判断队列是否满了
if(isFull()){
System.out.println("队列已满,不可加入数据");
return;
}
// 尾部后移
rear++;
arr[rear]=item;
}
// 出队列操作
public int getQueue(){
// 判断队列是否为空
if(isEmpty()){
throw new RuntimeException("队列为空,不可取数据");
}
front++;
return arr[front];
}
// 显示队列的所有数据
public void showQueue(){
// 遍历
if(isEmpty()){
throw new RuntimeException("队列为空,不可取数据");
}
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];
}
}
运行结果:
可以输入相要进行的操作,经过多次测试发现,当队列满了,然后把队列数据全部取出之后,虽然队列表示为空,但是不可再添加数据,即不能重复利用空间。
3、利用数组模拟循环队列(环形队列)
3.1、环形队列的定义(引用大话数据结构,资源我也上传了)
解决假溢出的办法急速后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
- 此时问题又出来了,我们刚才说,空队列时,front==rear,现在当队列满时,也是front等于rear,那么如何判断此时的队列究竟是空还是满呢?
- 办法一是设置一个标志变量flag,当front==rear,且flag=0时队列为空,当front=rear,且flag=1时为队列满。
办法二是当队列空时,条件就是front=rear,当队列满时,我们修改其条件,保留一个元素空间。也就是说,队列满时,数组中还有一个空闲单元。
- 由于rear可能比front大,也可能比front小,所以尽管它们只相差一个位置时就是满的情况,但也可能是相差整整一圈。所以若队列的最大尺寸QueueSize,那么
队列满的条件是(rear+1)%QueueSize==front(取模%的目的就是为了整合rear和front大小为一个问题)。
比如QueueSize=5,左上图中frnotallow=0,而rear=4,(4+1)%5=0,所以此时队列满。右上图,frnotallow=2而rear=1,(1+1)%5=2,所以此时队列也是满的。而对于下图,frnotallow=2而rear=0,(0+1)%5=1,1不等于2,所以此时队列并没有满。 - 另外,当rear>front时,即下图,此时队列的长度为rear-front,当rear<front时,队列长度分为两段,一段是QueueSize-front,另一段是0+rear,加在一起就是,队列长度为rear-front+QueueSize。
因此通用的计算队列长度公式为:(rear-front+QueueSize)%QueueSize
3.2、代码实现
3.2.1、创建一个类
定义四个变量,maxSize表示数组的最大容量,front指向队列的头部,rear就是队列尾部,arr存放数据模拟队列,并赋初始值。
class CircleQueue{
private int maxSize;
// 循环队列中的front跟顺序存储队列不一样,此时front表示指向队列第一个元素,即arr[front]就是第一个元素值
private int front;
// 此时rear表示指向队列的最后一个元素的后一个位置,空出一个空间作为约定
private int rear;
// 数组---》队列
private int[] arr;
public CircleQueue(int arrMaxSize){
maxSize=arrMaxSize;
arr=new int[maxSize];
front=0;
rear=0;
}
}
3.2.2、判断队列是否满或空
队列满的条件就是(rear+1)%maxSize==front
队列空的条件就是rear==front
// 判断队列是否满
public boolean isFull(){
return (rear+1)%maxSize==front;
}
// 判断队列是否空
public boolean isEmpty(){
return rear==front;
}
3.2.3、给队列添加数据
思路:首先判断队列是否满了,如果满了就不能添加数据,其次就是添加数据之后,rear要进行后移。(初始值为0),取模就是为了整合rear和front大小为一个问题。
// 入队列操作
public void addCircleQueue(int itm){
if(isFull()){
System.out.println("队列已满,不能添加数据");
return;
}
arr[rear]=itm;
// 后移 这里要考虑取模
rear=(rear+1)%maxSize;
}
3.2.3、出队列
思路:首先判断队列是否为空,如果为空,则不能取出数据,其次就是取出数据分为三步(front的初始值为0)
// 出对列操作
public int getCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
// 这里要分析出front是指向队列的第一个元素
// 1、先把front对应的值保存到临时变量
// 2、将front后移
// 3、将临时保存的变量返回
int res= arr[front];
front=(front+1)%maxSize;
return res;
}
3.2.4、队列的有效值个数
// 求出当前数据有效个数
public int size(){
return (rear+maxSize-front)%maxSize;
}
3.2.5、显示队列所有数据
思路:首先判断队列是否为空,如果不为空,从front开始遍历,遍历front+size个元素 为啥是front+size:因为size是有效值个数,所以front+size为循环终止条件 但是 输出的是arr[i%maxSize],而不是arr[i],因为i的值有可能大于maxSize,详情上循环队列的图,所以i也要取模。
// 展示队列
public void showCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
// 思路:从front开始遍历,遍历多少个元素
for(int i=front;i<(front+size());i++){
System.out.printf("a[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
3.2.6、显示对队列的头数据(注意不是取出数据)
// 展示头
public int headCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
return arr[front];
}
3.2.7、测试循环队列(解决假溢出)
public class ArrayCircleQueue {
public static void main(String[] args) {
CircleQueue circleQueue=new CircleQueue(3);
System.out.println("测试循环队列的案例~~~~");
char key=' ';// 接收用户输入
Scanner scanner=new Scanner(System.in);
boolean loop=true;
// 输出一个菜单
while (loop){
System.out.println("s(showCircle):显示队列");
System.out.println("e(exit):退出程序");
System.out.println("a(add):添加数据到队列");
System.out.println("g(get):从队列取出数据");
System.out.println("h(head):查看队列头的数据");
System.out.println("++++++++++++++++++++++++++++++++++++");
System.out.printf("输出你想要进行的操作:");
key =scanner.next().charAt(0); // 接收一个字符
switch (key){
case 's':
try {
circleQueue.showCircleQueue();
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'a': // 添加数据
System.out.println("输入一个数");
int value=scanner.nextInt();
circleQueue.addCircleQueue(value);
break;
case 'g': // 取出数据
try{
int res = circleQueue.getCircleQueue();
System.out.printf("取出的数据是%d\n",res);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'h': // 查看头数据
try{
int head=circleQueue.headCircleQueue();
System.out.printf("队列头数据显示为%d\n",head);
}catch (Exception e){
System.out.println(e.getMessage());
}
break;
case 'e':
scanner.close();
loop=false;
break;
default:
break;
}
System.out.println("程序退出~~");
}
}
}
class CircleQueue{
private int maxSize;
// 循环队列中的front跟顺序存储队列不一样,此时front表示指向队列第一个元素,即arr[front]就是第一个元素值
private int front;
// 此时rear表示指向队列的最后一个元素的后一个位置,空出一个空间作为约定
private int rear;
// 数组---》队列
private int[] arr;
public CircleQueue(int arrMaxSize){
maxSize=arrMaxSize;
arr=new int[maxSize];
front=0;
rear=0;
}
// 判断队列是否满
public boolean isFull(){
return (rear+1)%maxSize==front;
}
// 判断队列是否空
public boolean isEmpty(){
return rear==front;
}
// 入队列操作
public void addCircleQueue(int itm){
if(isFull()){
System.out.println("队列已满,不能添加数据");
return;
}
arr[rear]=itm;
// 后移 这里要考虑取模
rear=(rear+1)%maxSize;
}
// 出对列操作
public int getCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
// 这里要分析出front是指向队列的第一个元素
// 1、先把front对应的值保存到临时变量
// 2、将front后移
// 3、将临时保存的变量返回
int res= arr[front];
front=(front+1)%maxSize;
return res;
}
// 展示队列
public void showCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
// 思路:从front开始遍历,遍历多少个元素
for(int i=front;i<(front+size());i++){
System.out.printf("a[%d]=%d\n",i%maxSize,arr[i%maxSize]);
}
}
// 求出当前数据有效个数
public int size(){
return (rear+maxSize-front)%maxSize;
}
// 展示头
public int headCircleQueue(){
if(isEmpty()){
throw new RuntimeException("队列为空,不能取数据");
}
return arr[front];
}
}