数组模拟单向队列的思路及代码

数组,模拟,单向,队列,思路,代码 · 浏览次数 : 10

小编点评

1.测试--创建一个队列 ```java DirectionalArrQueue queue = new DirectionalArrQueue(4); 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.showAllMember(); case 'e' -> { scanner.close(); loop = false; System.out.println("程序退出"); } case 'a' -> { System.out.println("请输入一个整数"); int a = scanner.nextInt(); queue.addQueueMember(a); } case 'g' -> { try { int res = queue.getQueueMember(); System.out.println(res); } catch (Exception e) { System.out.println(e.getMessage()); } } case 'h' -> { try { int head = queue.getHeadMember(); System.out.println(head); } catch (Exception e) { System.out.println(e.getMessage()); } } } } ``` 2.测试--实现环形数组队列 ```java //环形数组队列的思路及代码 ```

正文

JAVA实现数组模拟单向队列的思路及代码

一、什么是队列?

队列是一种特殊的线性表 ,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。 进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列。 队列的数据元素又称为队列元素。

二、队列的特点及存储数据的方式

  1. 队列是一种先进先出的线性存储结构,即谁先入队谁就先出队列。

image-20230325154503356

三、创建单向数组队列类前思考

考虑到单向队列存取数据的特点,要想创建一个单向数组队列类,我们需要设立如下几个类属性:

  1. front:用于指向队列头部前一个的位置
  2. rear:用于指向队列尾部,用来指向队列尾部数据的一个指针
  3. maxSize:代表创建的该单向队列数组的最大存储容量
  4. front和rear的初始值都是-1。

以上属性的定义在如下代码预设计中可以体会的更加深刻,继续往下看!

四、单向数组队列类的预设计

①单向数组队列类的初始设计

/**
 * ClassName: directionalArrQueue
 * Package: com.zhao.test
 * Description:
 *
 * @Author XH-zhao
 * @Create 2023/3/25 16:04
 * @Version 1.0
 */
public class DirectionalArrQueue {

    private final int[] arrQueue;   // 创建的数组队列的地址引用
    private final int arrMaxSize;   // 代表创建的该单向队列数组的最大存储容量
    private int front;  // 用于指向队列头部前一个的位置
    private int rear;   // 用于指向队列尾部,用来指向队列尾部数据的一个指针

    /**
     * 单向数组队列构造函数
     * @param arrMaxSize 传入想要构造的队列长度
     */
    public DirectionalArrQueue(int arrMaxSize) {
        this.arrMaxSize = arrMaxSize;
        arrQueue = new int[this.arrMaxSize];
        // 将前后标记均指向队列的最前方(-1位置)
        front = -1;
        rear = -1;
    }

}

class test{
    public static void main(String[] args) {
        // 创建一个单向数组队列
        DirectionalArrQueue queue = new DirectionalArrQueue(4);
    }
}

通过以上单向数组队列类的设计,就可创建一个如下的单向数组队列对象。

②判断数组队列是否为空或为满

从前后两个指针标记角度考虑,由于rear的初始值为-1,增加队列成员的时候rear指针肯定要先向前移一位,再将队列成员的值添加到队列当中,在拿取队列成员的时候,front指针标记也是一样,要先向前移一位然后拿取对应指针下标标记的队列成员(由于对应下标的队列成员已经被取出,所以我们描述front的含义时说其总是指向队列头部前一个的位置)。

由上述增添和拿取队列成员的过程可以发现,每当两个指针相遇时(即rear == front),队列便是空的状态。

从上述增添队列成员过程中对rear标记的描述中,我们可以发现每次添加完队列成员后,rear标记总是指向队列尾部成员。所以我们可以推断出当rear位于数组队列最后的时候(即rear == arrMaxSize - 1),可以判定为队列已经满了。

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

/**
 * 判断该队列是否已经存满了数据
 * @return
 */
public boolean isFull() {
    return rear == arrMaxSize - 1;
}

代码测试:

public static void main(String[] args) {
    // 创建一个单向数组队列
    DirectionalArrQueue queue = new DirectionalArrQueue(4);

    // 判断队列是否为空
    boolean aNull = queue.isNull();
    System.out.println("队列是否为空:" + aNull);

    // 判断队列是否为满
    boolean full = queue.isFull();
    System.out.println("队列是否为满:" + full);
}

测试结果:

队列是否为空:true
队列是否为满:false

由于我们还没有完善增添和拿取队列成员的方法,所以此刻的单向队列queue肯定是空的。

③完善数组队列增添和拿取队列成员的方法

在充分理解②中我对增添和拿取队列成员的描述中,front和rear的含义以及标记的移动过程的情况下,理解如下增添和拿取队列成员的方法实现。

/**
 * 添加队列成员到队列
 *
 * @param member
 */
public void addQueueMember(int member) {
    // 先判断该队列是否满了,如果没满,则可以把数据添加到该队列
    if (isFull()) {
        System.out.println("该数列已满,不能再添加数据了");
        return;
    }
    // 该队列没满的情况下执行下面的代码
    rear++;
    arrQueue[rear] = member;
}

/**
 * 从队列中拿取队列成员
 *
 * @return
 */
public int getQueueMember() {
    // 从队列取队列成员时,先判断该队列是否为空
    if (isNull()) {
        // 如果执行到这里,就代表该队列是空的,所以执行时,抛出一个异常
        throw new RuntimeException("该队列为空,不能再进行出列操作了");
        // 抛出异常之后,就自己return了,所以不用再写return了,再在这个异常下面写代码就会
        // 之所以使用抛异常的方式结束,而不使用return 0;是因为防止成员队列就是数值0,产生误导
    }
    front++;    // front后移
    return arrQueue[front]; // 把从队列中取到的队列成员返回
}

后续为方便整体测试代码,不再逐步对方法进行测试,查看测试结果请继续往下学习!

④增加获取队列头成员和展示所有队列成员的方法实现

/**
 * 得到队列的第一个队列成员,即头部成员注意 不是取出头部成员,无需对front做++操作
 *
 * @return
 */
public int getHeadMember() {
    if (isNull()) {
        // 如果队列为空,则抛出异常
        throw new RuntimeException("该队列为空,取不到头部数据");
    }
    // 这里只是显示头部成员,所以front指针不往后移,只需要在arr[]中让front+1就可以了。
    return arrQueue[front + 1];
}

/**
 * 显示队列的所有成员
 */
public void showAllMember() {
    // 先判断该队列是否为空
    if (isNull()) {
        System.out.println("该队列为空,取不到数据");
        return;
    }
    for (int i = 0; i < arrQueue.length; i++) {
        System.out.printf("arr[%d]=%d\n", i, arrQueue[i]);
    }
}

五、实验测试单向数组队列的代码准确性

①单向队列实现以及测试的整体代码


import java.util.Scanner;

/**
 * ClassName: directionalArrQueue
 * Package: com.zhao.test
 * Description:
 *
 * @Author XH-zhao
 * @Create 2023/3/25 16:04
 * @Version 1.0
 */
public class DirectionalArrQueue {

    private final int[] arrQueue;   // 创建的数组队列的地址引用
    private final int arrMaxSize;   // 代表创建的该单向队列数组的最大存储容量
    private int front;  // 用于指向队列头部前一个的位置
    private int rear;   // 用于指向队列尾部,用来指向队列尾部数据的一个指针

    /**
     * 单向数组队列构造函数
     *
     * @param arrMaxSize 传入想要构造的队列长度
     */
    public DirectionalArrQueue(int arrMaxSize) {
        this.arrMaxSize = arrMaxSize;
        arrQueue = new int[this.arrMaxSize];
        // 将前后标记均指向队列的最前方(-1位置)
        front = -1;
        rear = -1;
    }

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

    /**
     * 判断该队列是否已经存满了数据
     *
     * @return
     */
    public boolean isFull() {
        return rear == arrMaxSize - 1;
    }

    /**
     * 添加队列成员到队列
     *
     * @param member
     */
    public void addQueueMember(int member) {
        // 先判断该队列是否满了,如果没满,则可以把数据添加到该队列
        if (isFull()) {
            System.out.println("该数列已满,不能再添加数据了");
            return;
        }
        // 该队列没满的情况下执行下面的代码
        rear++;
        arrQueue[rear] = member;
    }

    /**
     * 从队列中拿取队列成员
     *
     * @return
     */
    public int getQueueMember() {
        // 从队列取队列成员时,先判断该队列是否为空
        if (isNull()) {
            // 如果执行到这里,就代表该队列是空的,所以执行时,抛出一个异常
            throw new RuntimeException("该队列为空,不能再进行出列操作了");
            // 抛出异常之后,就自己return了,所以不用再写return了,再在这个异常下面写代码就会
            // 之所以使用抛异常的方式结束,而不使用return 0;是因为防止成员队列就是数值0,产生误导
        }
        front++;    // front后移
        return arrQueue[front]; // 把从队列中取到的队列成员返回
    }

    /**
     * 得到队列的第一个队列成员,即头部成员注意 不是取出头部成员,无需对front做++操作
     *
     * @return
     */
    public int getHeadMember() {
        if (isNull()) {
            // 如果队列为空,则抛出异常
            throw new RuntimeException("该队列为空,取不到头部数据");
        }
        // 这里只是显示头部成员,所以front指针不往后移,只需要在arr[]中让front+1就可以了。
        return arrQueue[front + 1];
    }

    /**
     * 显示队列的所有成员
     */
    public void showAllMember() {
        // 先判断该队列是否为空
        if (isNull()) {
            System.out.println("该队列为空,取不到数据");
            return;
        }
        for (int i = 0; i < arrQueue.length; i++) {
            System.out.printf("arr[%d]=%d\n", i, arrQueue[i]);
        }
    }

}

class test {
    public static void main(String[] args) {
        //测试--创建一个队列
        DirectionalArrQueue queue = new DirectionalArrQueue(4);
        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.showAllMember();
                case 'e' -> {
                    scanner.close();
                    loop = false;
                    System.out.println("程序退出");
                }
                case 'a' -> {
                    System.out.println("请输入一个整数");
                    int a = scanner.nextInt();
                    queue.addQueueMember(a);
                }
                case 'g' -> {
                    try {
                        int res = queue.getQueueMember();
                        System.out.println(res);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
                case 'h' -> {
                    try {
                        int head = queue.getHeadMember();
                        System.out.println(head);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                }
            }
        }
    }
}

②测试结果

s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
该队列为空,取不到数据
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
2
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
arr[0]=2
arr[1]=0
arr[2]=0
arr[3]=0
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
g
2
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
该队列为空,取不到数据
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
3
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
arr[0]=2
arr[1]=3
arr[2]=0
arr[3]=0
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
5
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
arr[0]=2
arr[1]=3
arr[2]=5
arr[3]=0
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
g
3
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
h
5
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
arr[0]=2
arr[1]=3
arr[2]=5
arr[3]=0
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
7
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
s
arr[0]=2
arr[1]=3
arr[2]=5
arr[3]=7
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
8
该数列已满,不能再添加数据了
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
g
5
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
g
7
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
g
该队列为空,不能再进行出列操作了
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据
a
请输入一个整数
2
该数列已满,不能再添加数据了
s(show):显示队列
e(exit):退出程序
a(add):添加数据到队列
g(get):从队列取出数据
h(head):查看队列头的数据

六、实验总结

从上述整体实验中可以看出,单向数组队列类的设计,完全呈现出了队列存储数据先进先出的特点。

单向数组队列的缺点

但在其中我们发现,单向数组队列存在一个明显的缺点,它是"一次性"的。在队列增添和拿取队列成员的过程中,前后标记指针在不断往队列数组的末端移动,导致就算队列数组前面的成员被取出之后,队列数组前面的数组空间不能复用,导致我们的队列顶多只能添加并拿取maxSize个成员。这很明显不是我们想要的!

那么我们如何将取出后的数组队列空间复用呢?

答案是实现环形数组队列!如何实现环形数组队列,我将在《JAVA实现数组模拟环形队列的思路及代码》一问中说明。

与数组模拟单向队列的思路及代码相似的内容:

数组模拟单向队列的思路及代码

JAVA实现数组模拟单向队列的思路及代码 一、什么是队列? 队列是一种特殊的线性表 ,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。 进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为

数组模拟环形队列的思路及代码

JAVA实现数组模拟环形队列的思路及代码 前言 在对Java实现数组模拟队列零了解的情况下,建议先去阅读《JAVA实现数组模拟单向队列的思路及代码》一文,可以辅助理解本文核心思想。 一、环形数组队列 实现:让数组达到复用的效果,即:当我们从数组队列中取出了数据,那取出数据后后这个空间可以再次使用。

【RocketMQ】消息的拉取总结

在上一讲中,介绍了消息的存储,生产者向Broker发送消息之后,数据会写入到CommitLog中,这一讲,就来看一下消费者是如何从Broker拉取消息的。 RocketMQ消息的消费以组为单位,有两种消费模式: 广播模式:同一个消息队列可以分配给组内的每个消费者,每条消息可以被组内的消费者进行消费。

LeetCode 双周赛 104(2023/05/13)流水的动态规划,铁打的结构化思考

本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 往期回顾:LeetCode 单周赛第 344 场 · 手写递归函数的通用套路 T1. 老人的数目(Easy) 标签:模拟、计数 T2. 矩阵中的和(Medium) 标签:模拟、排序 T3. 最大或值(Medi

NumPy 泊松分布模拟与 Seaborn 可视化技巧

泊松分布是描述单位时间间隔内随机事件发生次数的离散概率分布,参数λ表示平均速率。公式为 P(k) = e^(-λ) (λ^k) / k!。NumPy 的 `random.poisson()` 可生成泊松分布数据。当 λ 很大时,泊松分布近似正态分布。练习包括模拟顾客到达、比较不同 λ 下的分布及模拟...

[转帖]kafka压测多维度分析实战

设置虚拟机不同的带宽来进行模拟压测 kafka数据压测 1、公司生产kafka集群硬盘:单台500G、共3台、日志保留7天。 1.1 版本:1.1.0 2、压测kafka。 2.1 使用kafka自带压测工具:bin/kafka-producer-perf-test.sh 命令参数解释: --num

[转帖]jmeter_采样器sampler简介

1、取样器介绍 取样器是用来模拟用户操作的,向服务器发送请求以及接收服务器的响应数据。 取样器是在线程组内部的元件,也就是说取样器只能在线程组中添加。 取样器(Sampler)是性能测试中向服务器发送请求,记录响应信息,记录响应时间的最小单元。(取样器通常要进行这三个工作) 2、jmeter自带取样

vue3组件通信与props

title: vue3组件通信与props date: 2024/5/31 下午9:00:57 updated: 2024/5/31 下午9:00:57 categories: 前端开发 tags: Vue3组件 Props详解 生命周期 数据通信 模板语法 Composition API 单向数据

简单进行Springboot Beans归属模块单元的统计分析方法

简单进行Springboot Beans归属模块单元的统计分析方法 背景 基于Springboot的产品变的复杂之后 启动速度会越来越慢. 公司同事得出一个结论. beans 数量过多会导致启动速度逐渐变慢. 之前同事写过功能进行分析. 但是本着能不影响产品就不影响产品. 我想通过其他方式进行处理.

.NET Core MongoDB数据仓储和工作单元模式封装

前言 上一章我们把系统所需要的MongoDB集合设计好了,这一章我们的主要任务是使用.NET Core应用程序连接MongoDB并且封装MongoDB数据仓储和工作单元模式,因为本章内容涵盖的有点多关于仓储和工作单元的使用就放到下一章节中讲解了。仓储模式(Repository )带来的好处是一套代码