02-java数据结构之队列Queue的学习

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,左上图中front=0,而rear=4,(4+1)%5=0,所以此时队列满。右上图,front=2而rear=1,(1+1)%5=2,所以此时队列也是满的。而对于下图,front=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];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值