【数据结构】队列

news2025/4/27 16:09:45

1.啥是队列

2.队列实现

3.Queue接口的介绍以及队列的使用

4.相关队列的例子

(1)啥是队列

我们之前讲解了栈,栈和队列是有点区别的

我们说过栈是一种先进后出的数据结构,你可以把它想象成羽毛球筒;然而队列属于一种先进先出的数据结构,队列嘛,就跟你排队做核酸排队打饭一样咯,你先排,你就先桶嗓子眼,你先排你就先打到饭(当然是不能有插队的情况啦~队列中也没有插队的概念,不会说在中间位置插入元素)

 上图就是你每次去食堂打饭的样子,元素从队尾进,从队首出~~~


(2)队列的实现

队列的底层是一个链表,用节点来连接,LinkedLIst实现了Queue接口,因此可以使用Linkedlist来实现一个队列

队列分为:普通队列,双端队列(Deque),优先级队列(堆)

由于堆是之后会介绍的一种数据结构,这里我们就来说说普通队列和双端队列

普通队列就是排队咯,它只有一个方向,遵循先进先出的特点,而双端队列是有两个方向的,你可以同不同的方向进,但你然需要遵循先进先出的特点,简单来说:普通队列是单行道,双端队列是双行道~

 上图展示了双链表的强大之处,使用双链表我们可以实现普通队列,实现双端队列,还可以实现栈~

当然,双链表这么牛逼,使用来实现一个队列岂不是小菜一碟~所以我们提升一下难度,我们不用双链表实现队列,我们改用单链表来实现队列!!!!!


我们知道单链表的形式,可以头插尾插,然后还有一个头节点head(这不是虚拟头节点,而是代表第一个元素~)

接下来我们看看怎么使用一个单链表来实现队列,这里我们理想的方式是使队列的入队和出队操作的时间复杂度都分别达到O(1)(遵循先进先出的规则)

1.头插法入队,删除尾巴节点出队(先进先出)——头插的时间复杂度是O(1),我们有一个头节点head,因此不需要遍历,可以直接头插插入,因此我们使用头插入队;然后我们删除尾巴节点来出队,我们想要删除尾巴节点来实现出队,这样才能完成先进先出的特点,但是我们发现删除尾巴节点出队的话,需要遍历,因此出队操作的时间复杂度达到了O(N);这貌似不是我们想要的 0_0

2.尾插法入队,删除头节点出队(先进先出)——我们使用尾插法的话,我们每次插入都要去遍历去寻找最后一个节点,导致入队操作的时间复杂度达到了O(N),但我们删除头节点的时间复杂度却是O(1),可是吧,我们的初衷是想入队出队都达到O(1)哦~

3.那我们还能怎么办?当然是发挥我们的主观能动性,上述两个方式结合一下啦~我们仍然需要遵循先进先出的规定,既然这样,我们仍然采用尾插法入队,但这里要给尾巴节点设置一个tail标志,用来记住尾巴节点,这样每次插入才能直接找到尾巴节点,不需要遍历,从而使入队达到O(1),而我们删除头节点来出队,头节点随时被记录着,所以删除头节点来出队,自然是O(1)~

那么下面是自定义实现普通队列的代码(有注解)

/**
 * 使用单链表加上标记尾巴节点来实现出队和入队都是O(1)的队列
 */
public class MyQueue {

    //创建节点
    static class Node{
        public int val;
        public Node next;
        public Node(int val){
            this.val = val;
        }
    }

    //创建队列的头和尾
    public Node head;
    public Node tail;

    /**
     * 入队操作
     * 当没有元素的时候
     * 当有元素的时候
     */
    public void offer(int val){
        Node node = new Node(val);
        if(head == null){
            head = node;
            tail = node;
        }else {
            tail.next = node;//使用有标记的尾插,达到O(1)
            //tail = node;
            tail = tail.next;
        }
    }

    /**
     * 出队操作
     * 判断是否有节点的情况
     * 判断是否只有一个节点的情况
     * 判断有多个节点的情况
     * 出队是要返回值的,记得保存好头节点,删除头节点后要返回
     */
    public int poll(){
        if(head == null){
            return -1;//这里也可以抛出一个异常
        }
        //要先提前记录好头节点
        int oldVal = head.val;
        if(head.next == null){
            //这是只有一个节点的情况
            //直接让head和tail指向空即可
            /*head = null;
            tail = null;*/
            head = tail = null;//可以连在一起这样写
        }else {
            //这里是有两个节点或以上的情况
            //直接让head变为当前头节点的下一个节点即可
            head = head.next;
        }
        //最后这里要返回出队的原始头节点的值
        return oldVal;
    }

    /**
     * 查看当前队头元素
     */
    public int peek(){
        if(head == null){
            return -1;//这里也可以抛出一个异常
        }
        return head.val;
    }
}

以上就是使用单链表实现一个队列的简单操作,入队出队时间复杂度均为O(1)


(3)介绍Queue接口以及队列的使用

 

 (图片来源:比特高博)

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。

 


循环队列

 (图片来源:比特高博)

循环队列一般使用数组来实现

循环队列,同样要先进先出嘛,那么给定你一个数组,我们现在要往数组里面入队了(放元素)

 但是我们可以放元素,也同样可以出元素对吧,我们要放入55,然后33出队,然后我们不仅放不仅出,最后元素会放到数组的最后位置

 

 我们现在front走到倒数第二个位置了,然后把67放入最后一个位置,rear++,为空,表示数组满了,但是我们发现,前面还存在我们之前出队元素的空间!!问题是你的rear还无法返回到0下标的空间

为了解决这个问题,我们把数组给卷起来看~

 我们把数组卷起来后,正常的往里面添加元素,刚开始front和rear相遇,表示数组为空,当我们不停的放入元素后,假设是一个循环(可以从7到0)

我们发现此时front又再次和rear相遇了,那么此时数组到底是空还是满呢?我们无法判断

为此我们有两种方法来判断当前数组是否为满

1.计数方法:这是最好的一个办法,我们定义一个usedSize来记录有效元素的个数,每当放入一个元素,计数器usedSize++,当usedSize的大小等于数组的大小的时候,我们就知道数组满了 

2.浪费空间法:当我们插入元素到6下标后,rear++会走到7,此时我们就要判断rear的下一个位置是否是0,如果为0,那么我们就不再对7下标位置插入元素,浪费掉这个空间,然后判定数组为满

 计数器方法是最简单也是最好用的,只需要每次插入++一下就行了,接下来我们看看怎么浪费一个空间来实现这个循环队列

下面是代码展示

/**
 * 浪费空间的策略来设计循环队列
 */
public class MyCircularQueue {
    public int[] elem;
    //public int usedSize;计数
    public int front;//队头元素
    public int rear;//队尾元素

    public MyCircularQueue(int k){
        this.elem = new int[k+1];//数组初始化
    }
    //接下来是一堆方法

    /**
     * 入队
     * 要注意满的情况
     * 还要注意怎么从最后的下标跑到下标0从而实现循环的概念
     */
    public boolean enQueue(int value){
        if(isFull()){
            return false;
        }

        this.elem[rear] = value;
        //这里千万不能让rear++,要是++,你怎么从最后的下标跳到下标0?
        rear = (rear + 1) % elem.length;
        return true;
    }

    /**
     * 出队
     */
    public boolean deQueue(){
        if(isEmpty()){
            return false;
        }
        front = (front + 1) % elem.length;//这样才能循环
        return true;
    }

    /**
     * 获取队头元素
     * @return
     */
    public int Front(){
        if(isEmpty()){
            return -1;
        }
        return elem[front];
    }

    /**
     * 获取队尾元素
     * @return
     */
    public int Rear(){
        if (isEmpty()){
            return -1;
        }

        int index = (rear == 0) ? elem.length-1 : rear-1;
        return elem[index];
    }

    //判断满
    public boolean isFull(){
        return (rear + 1) % elem.length == 0;
    }

    //判断空
    public boolean isEmpty(){
        return front == rear;
    }
}

当我们每次插入元素的时候,我们不要让rear++,出元素的时候也不要直接让front++,如果直接让他们++,要怎么使7变到0从而完成循环效果?所以++是行不通滴,我们要使用一个式子

(当前位置+1)% 数组长度

假设现在rear在4下标位置,我们插入元素要4下标位置后,不能不让rear直接++到5的位置,而是 

(rear + 1) % len  =  (4+1)%8 = 5,我们发现rear正好到达5位置,那么我们想想我们插入7个元素后,此时rear在7位置下标,此时应该为满了,不能再插入了,因此当rear下一个位置是0下标的时候,就判断为满,使用上述这个式子,同样可以使rear过渡到0位置,(7+1)%8 = 0;

因此每次插入元素之前,都要判断是否为满,为空就是规定当front==rear的时候为空,因此每次删除元素的时候同样要去判断数组是否为空

当然,实现循环队列,还是那句话,使用计数器来实现是最好的(呜呜呜)

以上就是循环队列的实现和细节讲解

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

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

相关文章

一文读懂VMware虚拟化技术(含超融合)

1. 概述 1.1 为什么使用虚拟化 基于云服务器业务,很多公司不需要那么强大的服务器,将服务器虚拟化之后分开卖收益更高 比如租房,有一个100平面的房子,整租可以一个月房租8000,划分4个区域分这组,可以每个…

OAuth2.0

OAthu2.0参考链接1 OIDC(OpenId Connect)身份认证参考链接 一、定义 OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批…

《大数据分析-数据仓库项目实战》--阅读笔记

本文是《大数据分析-数据仓库项目实战》阅读笔记。 内容全部摘抄于本书。 算入门教材、文中有大量软件的安装步骤、对技术细节未过多涉及。 前言描述 大数据时代,需要考虑数据的采集、存储、计算处理等方式。 数据仓库建模方式:确定业务过程、声明粒度…

统计信号处理基础 习题解答6-10

题目: 我们继续习题6.9,考察在有色噪声环境下OOK系统信号选择的问题,令噪声 为零均值的WSS随机过程,ACF为 求PSD,并且对于 画出PSD的图形。和前一个系统一样,对于N50,求产生BLUE最小方差频率。提示&#x…

Espressif-IDE NameError: name ‘websocket‘ is not defined 解决方法

前言 ESP32 具有wifi 与 蓝牙,性价比比较高,一些嵌入式开发中经常用到,最近更新了一下 ESP32的开发环境, ESP32 开发工具下载地址:https://dl.espressif.cn/dl/esp-idf/ 下载文件:espressif-ide-setup-2.7…

HIve数仓新零售项目DWS层的构建(Grouping sets)模型

HIve数仓新零售项目 注:大家觉得博客好的话,别忘了点赞收藏呀,本人每周都会更新关于人工智能和大数据相关的内容,内容多为原创,Python Java Scala SQL 代码,CV NLP 推荐系统等,Spark Flink Kaf…

一文搞懂《前后端动态路由权限》

前言 本文主要针对后台管理系统的权限问题,即不同权限对应着不同的路由,同时侧边栏的路由也需要根据权限的不同异步生成。我们知道,权限那肯定是对应用户的,那么就会涉及到用户登录模块,所以这里也简单说一下实现登录的…

同花顺_代码解析_技术指标_S

本文通过对同花顺中现成代码进行解析,用以了解同花顺相关策略设计的思想 目录 SADL SAR SDLH SG_NDB SG_XDT SG_评分 SGSMX SG量比 SI SKDJ SRDM SRMI STIX SADL 腾落指数 1.ADL与指数顶背离时,指数向下反转机会大; 2.ADL与指…

合成孔径SAR雷达成像成(RDA和CSA)(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

(免费分享)基于springboot博客系统

源码获取:关注文末gongzhonghao,输入015领取下载链接 开发工具:IDEA,数据库mysql 技术:springbootmybatis-plusredis 系统分用户前台和管理后台 前台截图: 后台截图: package com.puboot.…

思泰克在创业板过会:拟募资4亿元,赛富投资、传音控股等为股东

11月18日,深圳证券交易所创业板披露的信息显示,厦门思泰克智能科技股份有限公司(下称“思泰克”)获得上市委会议通过。据贝多财经了解,思泰克的招股书于2022年5月5日获得创业板受理。 本次冲刺创业板上市,思…

西北工业大学算法理论考试复习

😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!&#x1f4…

阿里云免费SSL证书过期替换

阿里云上有免费的SSL证书,但是好像一个账号全部免费的额度只有20张,一张可以用1年,意思是如果20年后你还需要SSL证书的话,那么你可能就得买了。 我的SSL证书过期了,网站能访问,但是浏览器总是说站点不安全&…

【蓝桥杯冲击国赛计划第7天】模拟和打表 {题目:算式问题、求值、既约分数、天干地支}

文章目录1. 模拟和打表1.1 定义2. 实例「算式问题」题目描述运行限制2.1 简单分析2.2 检查函数2.3 三重化二重3. 实例「求值」题目描述运行限制3.1 简单分析3.2 主函数4. 实例「既约分数」题目描述运行限制4.1 简单分析4.2 辗转相除法2.3 主函数5. 实例「天干地支」题目描述输入…

同花顺_代码解析_技术指标_T、U

本文通过对同花顺中现成代码进行解析,用以了解同花顺相关策略设计的思想 目录 TBR TRIX TRIXFS TWR UDL UOS TBR 新三价率 新三价率:100*上涨家数/(上涨家数下跌家数) MATBR1:TBR的M1日异同移动平均 MATBR2:TBR的M2日异同移动平均 1.指数仍处于下跌状态&a…

Java数据结构 | PriorityQueue详解

目录 一 、PriorityQueue 二、PriorityQueue常用方法介绍 三、 PriorityQueue源码剖析 四:应用:Top-K问题 一 、PriorityQueue 常用接口介绍 上文中我们介绍了优先级队列的模拟实现, Java集合框架中提供了PriorityQueue和PriorityBlocki…

2021 XV6 4:traps

目录 1.RISC-V assenbly 2.Backtrace 3.Alarm 1.RISC-V assenbly 第一个任务是阅读理解,一共有6个问题。 1.Which registers contain arguments to functions? For example, which register holds 13 in mains call to printf? 具体来说就是a0,a1几个…

Docker入门

目录 Docker的作用 Docker的核心概念 Docker安装 镜像命令 镜像下载 查看镜像 搜索镜像 删除镜像 容器命令 创建容器 列出容器 新建并启动容器(最常使用) 守护态运行 启动容器 终止容器 重启容器 进入容器 attach命令 exec命令(最常使用) 退出容器…

【JavaEE】一文掌握 Ajax

🐱‍🏍目录1. AJAX 简介2. 伪造Ajax演示3. jQuery.ajax3.1 简单测试,使用最原始的HttpServletResponse处理3.2 使用ajax动态构建前端表格3.3 登录提示效果小demo4. 练习小demo,实现百度搜索框的动态内容提示5. 总结:1.…

纸牌游戏洗牌发牌排序算法设计

纸牌游戏洗牌发牌排序算法设计 本文提供纸牌游戏设计制作的基础部分,即洗牌,发牌,牌张排序排列显示的算法。 以及游戏开始时间使用时间的显示。我是用简单的C语言编译器MySpringC在安卓手机上编写的。此是游戏的框架,供游戏设计者…