Java 数据结构篇-用链表、数组实现队列(数组实现:循环队列)

news2025/1/19 11:25:08

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
 

文章目录

        1.0 队列的说明

        1.1 队列的几种常用操作

        2.0 使用链表实现队列说明

        2.1 链表实现队列

        2.2 链表实现队列 - 入栈操作

        2.3 链表实现队列 - 出栈操作

        2.4 链表实现队列 - 获取队头元素操作(不删除)

        2.5 链表实现队列 - 获取队列有效元素个数操作

        2.6 链表实现队列 - 判空处理操作

        2.7 用链表实现队列的完整代码

        3.0 使用数组实现循环队列说明

        3.1 数组实现循环队列的操作

        3.2 数组实现循环队列 - 入队列操作

        3.3 数组实现循环队列 - 出队列操作

        3.4 数组实现队列 - 得到队头元素操作(不删除)

        3.5 数组实现队列 - 得到队尾元素操作(不删除)

        3.6 数组实现队列 - 判空队列操作

        3.7 数组实现队列 - 判满队列操作

        3.8 用数组实现队列完整代码


        1.0 队列的说明

        队列是一种线性数据结构,具有先进先出First In First Out,FIFO)的特性。它类似于排队的概念,新元素被添加到队列的尾部而从队列中移除元素时则从队列的头部开始。队列通常用于需要按照特定顺序处理元素的场景,比如任务调度、消息传递等。

        1.1 队列的几种常用操作

                - 队首元素(front):队列的头部元素。

                - 队尾元素(rear):队列的尾部元素。

                - 入队(offer):将新元素添加到队列的尾部。

                - 出队(poll):从队列的头部移除元素。

                - 获取队头元素(peek):从队列的头部获取元素,不移除。

                - 判空(isEmpty):判断队列是否为空。

                - 判满(isFull):判断队列是否已满(对于有限大小的队列)。

                - 元素个数(size):获取队列有效的元素个数。

接口代码如下:

public interface QueueInterface{
    /**
     * 入列
     */
    boolean offer(int e);

    /**
     * 出列
     */
    int poll();

    /**
     * 获取队列头元素
     */
    int peek();

    /**
     * 获取队列中有效元素个数
     */
    int size();

    /**
     * 检验队列是否为空
     */
    boolean isEmpty();
}

        2.0 使用链表实现队列说明

        使用链表实现队列,无疑就是用链表的数据结构的方式来实现队列的操作(实现队列的接口)。跟栈不同的是:栈只能对一端进行操作,而队列是对头尾进行操作。一般对于单链表来说,头节点当作队列中的队头(front)尾节点当作队列中的队尾(rear)。而入队列相当于尾插,在链表尾部插入节点,出队列相当于头删,在链表头部删除头节点。对于双向链表来说,就比较自由了,头节点既可以作为队头,也可以作为队尾。尾节点既可以作为队头,也可以作为队尾。

        2.1 链表实现队列

        用双向链表来实现队列,既然头尾都可以作为队列,选其中一种即可,对于队尾来说也是如此。这里就用头节点作为队列中的队头,而尾节点作为队列中的队尾。

简单分析:

        - 实现入栈操作:在链表尾部插入节点即可。

        - 实现出栈操作:在链表头部删除节点即可。

        - 实现获取队列头部元素:获取链表头部节点的元素即可。

        - 实现总计有效元素个数:需要定义一个成员变量 size ,入栈时进行 size++ ,出栈时  size--

        - 实现判空处理:当 size == 0 ,则为空队列。

        - 实现判断满队列:当 size >= 创建队列时默认大小,则为满队列。

        2.2 链表实现队列 - 入栈操作

                实现入栈操作:在链表尾部插入节点即可。分为两种情况:当队列为空时,则入栈的节点即是队头也是队尾当队列不为空时,则入栈的节点一般来说就是新的队尾。每一次入栈完成后,都需要进行 size++

代码如下:

    //入列
    @Override
    public boolean offer(int e) {
        if (isEmpty()) {
            front = rear = new Node(null,e,null);
            size++;
            return true;
        }
        Node node = new Node(rear,e,null);
        rear.next = node;
        rear = node;
        size++;
        return true;
    }

        2.3 链表实现队列 - 出栈操作

                实现出栈操作:在链表头部删除节点即可。出栈一般分为三种情况:

        第一种情况,当队列为空时,不应该出栈了,直接返回 -1 或者抛异常。

        第二种情况,当只有一个节点时,出栈之后,就不会再有元素了,所以 frontrear 需要置为空。返回出栈节点的值即可。

        第三种情况,除了第一、二种情况之后,第三种情况就可以正常的进行头删节点操作处理了。需要注意的是,出栈完成后,进行 size-- 。最后,返回出栈节点的数值即可。

代码如下:

 

    //出列
    @Override
    public int poll() {
        if (isEmpty()){
            return -1;
        }
        //注意特例:只有一个节点的情况
        if (front.next == null) {
            int temp = front.val;
            front = null;
            rear = null;
            return temp;
        }
        Node temp = front;
        front = front.next;
        front.prev = null;
        size--;
        return temp.val;
    }

        2.4 链表实现队列 - 获取队头元素操作(不删除)

                获取链表头部节点的元素即可。一般分为两种情况:当队列为空时,可以返回 -1 或者抛异常处理当队列不为空时,直接返回头部节点的值即可

代码如下:

    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return front.val;
    }

        2.5 链表实现队列 - 获取队列有效元素个数操作

                直接返回 size 即可。

    @Override
    public int size() {
        return size;
    }

        2.6 链表实现队列 - 判空处理操作

                size == 0 时,返回 true 。否则返回 false 。也可以用 front == null 来判断是否为空队列。

代码如下:

    @Override
    public boolean isEmpty() {
        return front == null;
    }

 

        2.7 用链表实现队列的完整代码

public class MyLinkedQueue implements QueueInterface {

    static class Node {
        public Node prev;
        public int  val;
        public Node next;

        public Node(Node prev, int val, Node next) {
            this.prev = prev;
            this.val = val;
            this.next = next;
        }
    }

    private Node front;
    private Node rear;
    private int size;

    //入列
    @Override
    public boolean offer(int e) {
        if (isEmpty()) {
            front = rear = new Node(null,e,null);
            size++;
            return true;
        }
        Node node = new Node(rear,e,null);
        node.prev = rear;
        rear.next = node;
        rear = node;
        size++;
        return true;
    }

    //出列
    @Override
    public int poll() {
        if (isEmpty()){
            return -1;
        }
        //注意特例:只有一个节点的情况
        if (front.next == null) {
            int temp = front.val;
            front = null;
            rear = null;
            return temp;
        }
        Node temp = front;
        front = front.next;
        front.prev = null;
        size--;
        return temp.val;
    }

    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return front.val;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return front == null;
    }

}

 

        3.0 使用数组实现循环队列说明

        用数组实现队列,一般来说是循环队列,循环队列可以解决普通队列在出队操作后产生的空间浪费问题,同时可以利用数组的固定大小来实现队列的循环利用。

循环队列的接口代码如下:

public interface QueueInterface{
    /**
     * 入列
     */
    boolean offer(int e);

    /**
     * 出列
     */
    int poll();

    /**
     * 获取队列头元素,不删除
     */
    int peek();
    
    /**
     * 检验队列是否为空
     */
    boolean isEmpty();

    /**
     * 判断是否为满队列
     */
    boolean isFull();

    /**
     * 得到队尾元素,不删除
     */
    int Rear();
}

        使用数组实现队列,无疑就是用数组的数据结构的方式来实现队列的操作(实现队列的接口)。循环队列的关键是使用两个指针来标记队列的头部和尾部,分别称为 front rear 当入队时,rear 指针向后移动当出队时,front 指针向后移动。当 rear 指针到达数组的末尾时,可以通过取模运算将 rear 指针置为 0 ,实现循环的效果。

        3.1 数组实现循环队列的操作

                - 入队操作:将元素添加到 rear 指针所指向的位置,并更新 rear 指针。

                - 出队操作:front 指针所指向的位置移除元素,并更新 front 指针。

                - 判空操作:front 指针等于 rear 指针时,队列为空。

                - 判满操作: (rear+1) % 数组长度等于 front 指针时,队列为满。

        3.2 数组实现循环队列 - 入队列操作

                将元素添加到 rear 指针所指向的位置,并更新 rear 指针。更新 rear 就是往后走一步,但是为了实现循环,可不能简单的进行 +1 处理,需要 rear = (rear + 1) % arr.length,这样就可以数组中循环走了。在入队前,需要先判断是否为满队列了。

代码如下:

    // 入队列
    @Override
    public boolean offer(int e) {
        if (isFull()) {
            return false;
        }
        arr[rear] = e;
        rear = (rear+1) % arr.length;
        return true;
    }

         3.3 数组实现循环队列 - 出队列操作

                front 指针所指向的位置移除元素,并更新 front 指针。同样的,更新可不能简单的 +1 处理,需要将 front = (front + 1) % arr.length ,每次出栈前需要记录一下出栈的元素,接着才往后走,最后返回记录的元素即可。出栈前,需要判断是否为空队列,如果为空队列,就需要抛异常或者返回 -1 处理。

代码如下:

    //出队列
    @Override
    public int poll() {
        if (isEmpty()) {
            return -1;
        }
        int temp = arr[front];
        front = (front + 1) % arr.length;
        return temp;
    }

        3.4 数组实现队列 - 得到队头元素操作(不删除)

                先判断是否为空队列,若不是,则返回队头元素即可。

代码如下:

    //得到队头元素,不删除
    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[front];
    }

         3.5 数组实现队列 - 得到队尾元素操作(不删除)

                先判断队列是否为空,如果为空队列,则返回 -1 或者抛异常。如果不为空,则需要返回 arr[rear - 1],但是需要注意的是:有一种情况需要额外考虑进去,当 rear == 0 时,那么 rear - 1 == -1 所以不合理,数组中的索引不存在 -1 。因此这个情况要分开讨论,当 rear == 0 时,那么需要返回的值为 arr[arr.length - 1]当 rear != 0 时,那么返回的值为 arr[rear - 1]

代码如下:

    //得到队尾元素,不删除
    @Override
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        int temp = (rear == 0) ? arr.length - 1 : rear - 1;
        return arr[temp];
    }

        3.6 数组实现队列 - 判空队列操作

                当且仅当 rear == front 时,则队列为空

代码如下:

    @Override
    public boolean isEmpty() {
        return front == rear;
    }

        3.7 数组实现队列 - 判满队列操作

                有很多种方式可以来判断队列是否满,比如,定义 size 变量来总计元素个数,然后跟 arr.lengrh 进行对比,相等即满队列了,不相等即为还没有满队列使用标记也可以实现判断是否为满队列。

        这里介绍牺牲空间来判断满队列,即当且仅当 (rear + 1) % arr.lengrh == front 时,该队列满了。由于这里牺牲了一个空间,所以需要在自定义初始化数组大小的时候额外 +1

代码如下:

    // 自定义数组大小
    public ArrayQueue(int capacity) {
        arr = new int[capacity + 1];
    }

    // 默认数组大小为: 10
    public ArrayQueue() {
        arr = new int[10];
    }
    @Override
    //判断是否为满队列(有很多种判断方法,这里使用牺牲空间方法来判断)
    public boolean isFull() {
        return (rear + 1) % arr.length == front;
    }

        3.8 用数组实现队列完整代码

public class ArrayQueue implements QueueInterface{

    private int front;
    private int rear;
    private int[] arr;

    // 自定义数组大小
    public ArrayQueue(int capacity) {
        arr = new int[capacity + 1];
    }

    // 默认数组大小为: 10
    public ArrayQueue() {
        arr = new int[10];
    }

    // 入队列
    @Override
    public boolean offer(int e) {
        if (isFull()) {
            return false;
        }
        arr[rear] = e;
        rear = (rear+1) % arr.length;
        return true;
    }

    //出队列
    @Override
    public int poll() {
        if (isEmpty()) {
            return -1;
        }
        int temp = arr[front];
        front = (front + 1) % arr.length;
        return temp;
    }

    //得到队头元素,不删除
    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[front];
    }

    //得到队尾元素,不删除
    @Override
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        int temp = (rear == 0) ? arr.length - 1 : rear - 1;
        return arr[temp];
    }

    @Override
    public boolean isEmpty() {
        return front == rear;
    }

    @Override
    //判断是否为满队列(有很多种判断方法,这里使用牺牲空间方法来判断)
    public boolean isFull() {
        return (rear + 1) % arr.length == front;
    }
    
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1280459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

养身馆推拿会员管理系统,佳易王推拿会员管理软件短信设置教程

养身馆推拿会员管理系统,佳易王推拿会员管理软件短信设置教程 一、佳易王会员管理软件大众版 部分功能简介: 1、会员信息登记 :可以直接使用手机号登记,也可以使用实体卡片,推荐用手机号即可。 2、会员卡类型 &…

【每日一题】可获得的最大点数

文章目录 Tag题目来源题目解读解题思路方法一:滑动窗口方法二:前缀和 写在最后 Tag 【滑动窗口】【前缀和】【数组】【2023-12-03】 题目来源 1423. 可获得的最大点数 题目解读 在一排卡牌中拿出 k 张卡牌,每次必须从这一排卡牌的开头或者…

运维02:Linux

Linux安装 VMWare安装:夸克网盘分享(提取码:refg) CentOS安装:Index of /centos/7.9.2009/isos/x86_64/ Xshell安装:百度网盘 请输入提取码(提取码:juau) 环境准备 1、…

Pikachu(三)

RCE(remote command/code execute)概述 RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。 远程系统命令执行 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口 比如我…

Cmkae外部依赖管理

文章目录 一、cmake依赖管理介绍二、源码依管理1. FetchContent与find_package进行集成 2. CPM3. git submodule附加: address_sanitizer 和 undefined sanitizer 一、cmake依赖管理介绍 CMake 是跨平台的构建系统,支持 C/C、Objective-C、Fortran 等多种…

C++基础 -36- 模板之模板函数

模板函数格式 template <class T> void allexchange(T a,T b) {T c;c*a;*a*b;*bc; }模板函数可以增强函数的通用性 举例说明&#xff0c;使用一个模板函数实现了两个的函数的功能 #include "iostream"using namespace std;void myexchangeint(int* a,int* …

Grafana部署与Zabbix集成,搭建开源IT监控平台

Grafana部署与Zabbix集成 目前在一家公司主要是网络、运维、IT支持&#xff0c;每次需要检查服务器状态都是需要手动登录系统进行查看&#xff0c;因此想着部署一套监控系统&#xff0c;功能上需要实现监控、可视化、告警等。由于预算没有&#xff0c;服务器资源倒是有空闲的&a…

【hacker送书活动第7期】Python网络爬虫入门到实战

第7期图书推荐 内容简介作者简介大咖推荐图书目录概述参与方式 内容简介 本书介绍了Python3网络爬虫的常见技术。首先介绍了网页的基础知识&#xff0c;然后介绍了urllib、Requests请求库以及XPath、Beautiful Soup等解析库&#xff0c;接着介绍了selenium对动态网站的爬取和S…

电容和电感

一、电感 1&#xff09;图片 2&#xff09;作用 a&#xff09;储存容量 例如dcdc转换器的原理,将一个电压值转换成另外一个电压值 b&#xff09;选择信号 比如空气中弥漫着很多信号&#xff0c;我们应该怎么选取我们所需要的信号。 电感和电容可以看成一个电阻&#xff0c;当电…

Redis ziplist源码解析

area |<---- ziplist header ---->|<----------- entries ------------->|<-end->|size 4 bytes 4 bytes 2 bytes ? ? ? ? 1 byte--------------------------------------------------------------- comp…

MySQL进阶部分

存储引擎 MySQL体系结构图&#xff1a; 连接层&#xff1a; 最上层是一些客户端连接服务&#xff0c;主要完成一些类似于连接处理 &#xff0c;授权认证及相关的安全方案。服务器也会为安全接入的每个用户端验证它所具有的操作权限。 服务层&#xff1a; 第二层架构主要完成大…

数据科学:Matplotlib、Seaborn笔记

数据科学&#xff1a;Numpy、Pandas、Matplotlib、Seaborn 三、Matplotlib1.Matplotlib subplots函数2.tight_layout()函数3.Matplotlib grid()设置网格格式4.fill_between()函数示例设置x轴为时间刻度热力图 四、Seaborn1.set2.seaborn.scatterplot 参考 数据科学&#xff1a;…

github打不开,全网最简单解决方法,没有之一

下载watt toolkit&#xff0c; 选择‘github’&#xff0c;点击‘一键加速’&#xff0c; 具体步骤如下&#xff1a;去电脑微软商店下载watt toolkit&#xff0c;或者直接打开网址https://apps.microsoft.com/detail/9MTCFHS560NG?hlen-us&glUS 如图&#xff0c;点击安装i…

Sun Apr 16 00:00:00 CST 2023格式转换

Date date new Date(); log.info("当前时间为:{}",date); //yyyy-MM-dd HH:mm:ss SimpleDateFormat sdf new SimpleDateFormat(DateUtils.YYYY_MM_DD_HH_MM_SS); String dateTime s…

Android11适配已安装应用列表

Android11适配已安装应用列表 之前做过已安装应用列表的适配&#xff0c;最近国内版SDK升级到33和隐私合规遇到很多问题&#xff0c;于是把已安装应用列表记录一下&#xff1a; 1、在Android11及以上的适配&#xff1a; package com.example.requestinsttallapplistdemoimpo…

电磁兼容EMC理论基础汇总

目录 0. 序言 1. EMC的基础介绍 1.1 EMC电磁兼容的定义 1.2 EMC的重要性 1.3 EMC的三要素 2. 库仑定律 3. 趋肤效应与趋肤深度 4. 电阻抗公式 4.1 电阻 4.2 容抗 4.3 感抗 4.4 电路元件的非理想性 5. 麦克斯韦方程组 5.1 高斯磁定律 5.2 高斯定律 5.3 法拉…

一文讲透Python函数的创建和调用

1.Python提供了函数作为完成某项工作的标准化代码块 Python本质上是一种编程语言&#xff0c;通过编写运行代码的方式实现工作目标。读者可以想象&#xff0c;如果针对机器学习或数据统计分析的每种方法或统计量计算都要用户自行编写代码&#xff0c;那么显然在很多情况下是无…

Rust 语言:认识 Rust

本心、输入输出、结果 文章目录 Rust 语言&#xff1a;认识 Rust前言Rust的特点Rust LOGO Rust 在IT行业的应用前景Rust 是一门系统级编程语言相关链接花有重开日&#xff0c;人无再少年实践是检验真理的唯一标准 Rust 语言&#xff1a;认识 Rust 编辑&#xff1a;简简单单 Onl…

多级缓存自用

1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈 •Redis缓存失效时,会对数据库产生冲击 多级缓存就是充分利用请求处理的每个环节,添加缓…

python系统调用执行ping命令无法检测到超时情况(破案了:ping命令-W参数单位为s,我写了个1000)

文章目录 问题描述破案了&#xff1a;ping命令-W参数单位为s&#xff0c;我写了个1000。。。,,ԾㅂԾ,, 问题描述 我用了系统调用去执行ping&#xff0c;一开始用os.system()&#xff0c;有问题&#xff0c;后面用subprocess问题还是存在&#xff0c;后来我把这个改了&#xff…