队列 - 数据结构与算法 - java

1、什么是队列?

  1. 队列是一个有序列表,可以用数组或是链表来实现。
  2. 遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出 3) 示意图:(使用数组模拟队列示意图)

2、队列的应用场景?

在银行的柜台,需要使用队列进行实现

3、使用Java 进行队列的创建等一系列操作

3.1、创建的队列类

package com.luobin.DataStructure.队列;

/**
 * @author LuoBin
 * @version 1.0
 * @date 2021/8/2 8:54 下午
 */
public 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("队列是满的,不能加入数据!");
        }

        rear++;     // 将标进行向后移动,添加数据
        arr[rear] = n;
    }

    // 获取队列中的数据,出队列
    public int getQueue() {
        if (isEmpty()) {
            // 通过抛出异常进行控制
            throw new RuntimeException("队列为空,不能取出数据");
        }

        front++; // 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];
    }
}

3.2、对创建的对象类进行测试相关的功能(main 方法实现)

package com.luobin.DataStructure.队列;

import java.util.Scanner;

/**
 * @author LuoBin
 * @version 1.0
 * @date 2021/8/2 9:22 下午
 */
public class TestQueue {
    public static void main(String[] args) {
        // 对于队列进行测试
        // 创建一个队列
        ArrayQueue queue = 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("Input a char: ");
            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", res);
                    } catch (Exception e) {
                        // 获取到前面写的提示,数据是空的,不能获取到
                        System.out.println(e.getMessage());
                    }
                case 'h':
                    try {
                        int res = queue.headQueue();
                        System.out.printf("队列头部的数据是:%d",res);

                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close(); // 关闭输入
                    loop = false;    // 退出
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");

    }
}

4、对于上述的代码进行优化处理

4.1、存在的问题

  1. 目前数组使用一次就不能用, 没有达到复用的效果
  2. 将这个数组使用算法,改进成一个环形的队列 取模:%

4.2、优化代码(相关思路)

在这里插入图片描述

4.2.1、队列满时的条件

为了方便起见,约定:初始化建空队时,令
front=rear=0,
当队空时:front=rear
当队满时:front=rear 亦成立
因此只凭等式front=rear无法判断队空还是队满。 有两种方法处理上述问题:
(1)另设一个标志位以区别队列是空还是满。
(2)少用一个元素空间,约定以“队列头指针front在队尾指针rear的下一个位置上”作为队列“满”状态的标志。即:

队空时: front=rear
队满时: (rear+1)%maxsize=front

下面的图形表示了,环形队列中存储数据的状态:
在这里插入图片描述

4.2.2、获取有效数据个数

数组实现环形队列-获取有效数据个数

rear队尾元素下标的下一个
front队头元素的下标
maxSize队列的最大长度
+maxSize防止结果为负数
%maxSize防止size大于maxSize

如果把这个分段函数写成一个函数的时候,
size = (rear-front+maxSize)%maxSize

4.3、代码实现:

4.3.1、环形队列类的实现

package com.luobin.DataStructure.队列;

/**
 * @author LuoBin
 * @version 1.0
 * @date 2021/8/3 3:38 下午
 */
public class CircleArrayQueue {
    private int maxSize; // 队列的最大值
    private int front;   // 初始值为 0
    private int rear;    // rear 指向队列的最后一个元素的后一个位置 ,中间空下一个,使用

    private int[] arr;// 用于存放数据

    // 创建队列的构造器
    public CircleArrayQueue(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[maxSize];
        front = 0; // 指向队列的头部
        rear = 0;  // 指向队列的尾部,刚开始头部和尾部是重合的
    }

    public CircleArrayQueue() {
    }

    // 判断队列是否是满的
    public boolean isFull() {
        // 此处的判断是否为满的条件,比较特殊
        // rear 后面的有一个是空的
        return (rear + 1) % maxSize == front;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    // 将数据添加到队列
    public void addQueue(int n) {
        // 判断是否是满的
        if (isFull()) {
            System.out.println("队列是满的,不能加入数据!");
        }

        // 直接将数据加入
        arr[rear] = n;
        // 将 rear 进行后移的操作,使用了取模,使得,数据可以进行循环存放,前面有了空位置之后
        // rear 是可以无限大的,但是存放的数据一直在循环
        rear = (rear + 1) % maxSize;
    }

    // 获取队列中的数据,出队列
    public int getQueue() {
        if (isEmpty()) {
            // 通过抛出异常进行控制
            throw new RuntimeException("队列为空,不能取出数据");
        }

        // 需要分析 front 是指向队列的第一个元素
        // 1、把 front 保存到临时变量
        // 2、将front 后移,front 不能无休止的向后移动,使用取模,控制在环形的队列中
        // 3、将临时变量返回
        int value = arr[front];
        front = (front + 1) % maxSize;
        return value;

        /*
             为什么不能写成

                return arr[front];
                front = (front+1) % maxSize;
                这种语句不成立,因为 return 之后的代码不会执行

                写成
                front++;
                return arr[front];
                没有考虑,环形队列的问题,不成立

                写成
                front = (front + 1) % maxSize;
                return arr[front];
                返回的不是第一个元素,不成立

                综上,写成上面的形式
         */
    }

    // 显示队列的所有数据
    public void showQueue() {
        if (isEmpty()) {
            System.out.println("队列是空的,没有数据");
            return;
        }


        //从 front 进行遍历,遍历 size() 停止
        for (int i = front; i < front + size(); i++) {
            // i 变成 i % maxSize ,控制不要超过了队列的大小
            System.out.printf("arr[%d]=%d\n", i % maxSize, arr[i % maxSize]);
        }

    }

    // 求出当前队列中的存在的数据,否则无法完成遍历的功能
    public int size() {
        // rear = 1
        // front = 0
        // maxSize = 3
        return (rear + maxSize - front) % maxSize;
    }


    // 显示队列的头数据,注意不是取出数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列是空的,没有数据");
        }
//        return arr[front + 1];
        // 此处的front 代表的是队列的第一个元素,以前是 -1 开始,现在是 0 开始,不需要进行 +1 的操作
        return arr[front];
    }


}

4.3.2、环形队列的测试

package com.luobin.DataStructure.队列;

import java.util.Scanner;

/**
 * @author LuoBin
 * @version 1.0
 * @date 2021/8/3 3:44 下午
 */
public class TestCircleArrayQueue {
    public static void main(String[] args) {
        System.out.println("对于环形队列的测试代码");
        // 对于队列进行测试
        // 创建一个队列
        // 4 是最大的空间,只能存储两个数据,因为预留了一个空的位置
        CircleArrayQueue queue = new CircleArrayQueue(4);
        char key = ' '; // 用于用户的出入操作
        Scanner scanner = new Scanner(System.in);

        boolean loop = true;
        while (loop) {
            System.out.println("\ns(show) : 显示队列");
            System.out.println("e(exit) : 退出程序");
            System.out.println("a(add) : 添加数据到队列");
            System.out.println("g(get) : 从队列取出数据");
            System.out.println("h(head) : 查看队列的头数据");

            System.out.println("Input a char: ");
            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());
                    }
                case 'h':
                    try {
                        int res = queue.headQueue();
                        System.out.printf("队列头部的数据是:%d",res);

                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    scanner.close(); // 关闭输入
                    loop = false;    // 退出
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");

    }
}

posted @   YIMENG-0  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示