【数据结构与算法】队列-模拟实现队列以及设计循环队列

news2024/11/17 1:52:05

文章目录

  • 队列的概念
  • 链表实现栈
  • 设计循环队列
  • 总结

队列的概念

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列是一种先进先出的数据结构,注意和栈进行区分,不要记混.

队列的实现有链式结构和顺序结构,接下来会使用链表和数组分别实现队列

队列中的方法有以下这些:

方法描述
offer(E e)入队列
poll()出队列
peek()获取队头元素
int size()获取队列中有效元素个数
boolean isEmpty()检测队列是否为空

接下来来实现这些功能

链表实现栈

链表实现栈有以下点需要注意:

  • 入队只能在队尾增加元素,可以遍历到最后一个元素,再进行增加元素 但每次入队都要进行遍历,就会很慢,为了解决这个问题,可以用tail记录下来最后一个元素,这样入队时,直接用tail就可以了
  • 在出队时要注意空指针异常,也就是当前队列为空的情况 我这里解决的方法是使用自定义异常也可以使用别的方法来解决
  • 除了空队列的这种情况,还要考虑只有一个元素的情况,如果入队的元素是第一个,要将head和tail同时指向要入队的结点. 如果当前队列只有一个元素,进行出队时,要将head和tail都置为null

代码如下:

public class MyQueue extends MyNullException{
    static class Node{
        public int val;
        public Node next;

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

    public Node head;
    public Node tail;

    /**
     * 入队
     * add与offer的区别
     * @param val
     */
    public void offer(int val){
        Node node = new Node(val);
        if (isEmpty()){
            head = node;
            tail = node;
        }else {
            tail.next = node;
            tail = tail.next;
        }
    }

    /**
     * 出队
     * @return
     */
    public int poll(){
        if (isEmpty()){
            throw new MyNullException("当前队列为空,出队失败!");
        }
        int ret = head.val;
        if (head.next == null){
            head = null;
            tail = null;
        }else {
            head = head.next;
        }
        return ret;
    }

    /**
     * 查看队头元素
     * @return
     */
    public int peek(){
        if (isEmpty()){
            throw new MyNullException("当前队列为空,查看失败!");
        }
        return head.val;
    }

    public boolean isEmpty(){
        return head == null;
    }

    public int size(){
        int ret = 0;
        Node cur = head;
        while (cur != null){
            ret++;
            cur = cur.next;
        }
        return ret;
    }
}
public class MyNullException extends RuntimeException{
    public MyNullException(){

    }
    public MyNullException(String message){
        super(message);
    }
}

设计循环队列

接下来用数组来实现队列,用数组来实现队列注意是采用循环队列这种方式
在这里插入图片描述
一般情况下,这样实现队列会有什么问题,首先队列出队是从队头出元素,队尾新增元素,出队时只需要front往前走一步,入队只需要让数组的rear下标设置为要增加元素的值,再让rear往后走一步就行,但是数组的长度是有限的,如果当前数组满了,就需要进行扩容,但之前如果频繁出队会造成数组的前面空间大量的浪费.
因此为了解决这个问题,推荐使用数组设计一个循环队列,循环队列最大的特点就是 front 可以不从0下标开始放数据,而rear也可以在队列不满的情况下,即使到了数组的最后一个元素,可以接着从0下标开始存放数据
看下图:
在这里插入图片描述
上图可知:front是从1下标开始存放元素的,如果此时rear下标是9,在入队一个元素,如何让9到0下标呢?此时就不能只让rear自增1了,而要让rear+1后进行取余

入队时: rear = (rear+1) % 数组长度;
出队时: front = (front+1) % 数组长度;

那么接下来要考虑一个问题,什么时候这个队列是满的?front等于rear?
front等于rear也可能是空队列,为了解决这个问题,有两种方法:

  1. 使用计数器,将数组中元素的有效个数记录下来
  2. 浪费一个空间,也就是当 (rear+1) % 数组长度== front 时为队列满这种情况

代码如下:

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];
    }

    /**
     * 入队
     * 如果需要扩容,就需要考虑从哪里开始拷贝
     * @param val
     * @return
     */
    public boolean enQueue(int val){
        if (!isFull()){
            elem[rear] = val;
            rear = (rear+1) % elem.length;
            // usedSize++;
            return true;
        }else{
            return false;
        }
    }

    /**
     * 出队
     * @return
     */
    public boolean deQueue(){
        if (isEmpty()){
            return false;
        }else {
            front = (front+1) % elem.length;
            // usedSize--;
            return true;
        }
    }

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

    /**
     * 获取队尾元素
     * 注意下标: 0和elem.length的时候
     * @return
     */
    public int Rear(){
        if (isEmpty()){
            return -1;
        }
        return (rear == 0) ? elem[elem.length-1] : elem[rear-1];
    }
    /**
     * 判断当前队列是否满了
     * @return
     */
    public boolean isFull(){
        /*if (usedSize == elem.length){
            return true;
        }
        return false;*/

        return (rear+1) % elem.length == front;
    }

    /**
     * 判断队列是否为空
     * @return
     */
    public boolean isEmpty(){
        /*if (usedSize == 0){
            return true;
        }
        return false;*/

        return rear == front;
    }
}

总结

  • 队列是先进先出的数据结构,不要和栈记混淆了.
  • 掌握链表实现栈,以及循环队列的原理
  • 熟悉循环队列中什么情况下队列满了 出队和入队时 front和rear 怎么进行调整

在这里插入图片描述

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

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

相关文章

图的搜索(DFS、BFS)

图的搜索(图的遍历)是指从图的任一顶点出发,访问图的所有顶点,且每个顶点只访问一次。 深度优先搜索 DFS概念: 深度优先搜索 (Depth-First Search,DFS)是从某个顶点v1出发对图进行搜素,每一步…

第八章 面向对象编程(中级)

一、访问修饰符(P279) 1. 基本介绍 java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围): (1)公开级别:用 public 修饰…

2018-NIPS-owards Sparse Hierarchical Graph Classifiers

2018-NIPS-owards Sparse Hierarchical Graph Classifiers Paper: https://arxiv.org/abs/1811.01287 Code: 对稀疏分类分级图 作者提出以往的图分类方法中通常使用单个全局池化步骤来聚合节点特征或手动设计的固定启发式算法,这样做会丢失信息,所以将…

ABB机器人系统输入输出信号System Input和Output详解(二)

ABB机器人系统输入输出信号System Input和Output详解(二) 上一次和大家分享了系统输入信号System Input相关的内容,具体可参考以下链接中的内容: ABB机器人系统输入输出信号System Input和Output详解(一) 本次和大家分享系统输出信号的相关内容: System Output类型: 可…

数据挖掘,计算机网络、操作系统刷题笔记38

数据挖掘,计算机网络、操作系统刷题笔记38 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发,测开 测开的话,你就得学数据库,sql,orac…

91年的印度程序员开发博客网站每月已赚2500美元以及他的创业历程

他是谁 Sai Krishna ,程序员、美食家、电影爱好者。 目前正在开发和维护的superblog.ai的收入情况(截止2023年1月27日): 此前他是 SpotPlay 的联合创始人兼首席技术官。此外,他开发的应用程序和游戏的下载量已达数百…

CodePlus | C# 网页所有图片批量下载

C# 网页所有图片批量下载 文章目录C# 网页所有图片批量下载前言演示效果操作步骤第一步:安装CodePlus扩展库第二步:提取链接程序第三步:取网页源码第四步:设置前后缀第五步:执行下载更多演示结束语前言 今天想着换一个…

CANoe-添加自定义示例工程

CANoe自带的示例工程我们讲过很多次了。当安装CANoe软件时,会在安装界面让用户选择是否安装Sample Configurations。如果勾选了,则会在电脑的的C盘自带需要的示例工程,路径为:C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 15.3.89 当然你也可以在CANoe软…

Netty 之 DefaultPromise 源码解析

在解析Netty源码时&#xff0c;在解析NioEventLoop 创建过程中&#xff0c;有一段这样的代码。 protected MultithreadEventExecutorGroup(int nThreads, Executor executor,EventExecutorChooserFactory chooserFactory, Object... args) {if (nThreads < 0) {throw new I…

Java深拷贝和浅拷贝Map对象

目录1 将Map深拷贝到另一个Map对象当中2 浅拷贝Map1 将Map深拷贝到另一个Map对象当中 今天赋值的时候遇到的小坑 相关文章推荐&#xff1a; Java克隆方式避免频繁创建对象优化方案 https://blog.csdn.net/ZGL_cyy/article/details/126556907 1.需求说明 将一个MapA对象中所…

Fork之前创建了互斥锁,要警惕死锁问题

文章目录Fork之前创建了互斥锁&#xff0c;要警惕死锁问题使用GDB进行调试如何解决该问题&#xff1f;是否还有别的问题&#xff1f;结论参考文献Fork之前创建了互斥锁&#xff0c;要警惕死锁问题 下面的这段代码会导致子进程出现死锁问题&#xff0c;您看出来了吗&#xff1f…

【MFC】使用MFC框架(10)

MFC不仅仅是一个类库&#xff0c;而且是一个所谓的“设计框架”&#xff0c;注入了很多开发理念和设计思想。类库与框架的区别可以理解为“食材”与“火锅”套餐的区别——火锅套餐已经标明了开发者必须接受已定的一些规则&#xff0c;包括“Message Mapping消息映射机制”、“…

忽略语法细节,从整体上理解函数

从整体上看&#xff0c;C语言代码是由一个一个的函数构成的&#xff0c;除了定义和说明类的语句&#xff08;例如变量定义、宏定义、类型定义等&#xff09;可以放在函数外面&#xff0c;所有具有运算或逻辑处理能力的语句&#xff08;例如加减乘除、if else、for、函数调用等&…

配置中心-开源系统对比分析

一、为什么需要配置中心 1、配置实时生效 传统的静态配置方式要想修改某个配置只能修改之后重新发布应用&#xff0c;要实现动态性&#xff0c;可以选择使用数据库&#xff0c;通过定时轮询访问数据库来感知配置的变化。轮询频率低感知配置变化的延时就长&#xff0c;轮询频率…

运放电路中电容的作用-运算放大器

在运放电路中&#xff0c;大家可能会经常看到这么几个电容&#xff0c;分别是&#xff1a; 1、电源VCC到地 2、反馈输入输出引脚之间 3、正负两输入端之间的电容 就算不要这几个电容&#xff0c;电路好像也能工作&#xff0c;但电路设计一般都会加上&#xff0c;那么这几个电…

软件无线电之数字下变频(Matlab实例)

软件无线电之数字下变频 1 原理 在通信系统中&#xff0c;为了易于信号发射以及实现信道复用&#xff0c;传输的信号发射频率一般很高。 在接收机中&#xff0c;为了降低信号的载波频率或是直接去除载波频率得到基带信号&#xff0c;通常将接收信号与本地振荡器产生的本振信…

Java循环综合案例

文章目录Java循环综合案例案例一&#xff1a;逢 7 跳过案例二&#xff1a;数组元素求和案例三&#xff1a;判断两个数组是否相同案例四&#xff1a;查找元素在数组中的索引案例五&#xff1a;数组元素反转案例六&#xff1a;评委打分案例七&#xff1a;随机产生验证码Java循环综…

那些年我们拿下了 Zynq

小菜鸟的 Zynq 学习经验分享~ 资料来源&#xff1a;黑金 Zynq7035 开发板配套资料&#xff0c;完全适合于 Zynq 学习。 获取方式&#xff1a;【51爱电子】回复【Zynq7000】即可获取资料链接&#xff01;本资料仅供学习使用&#xff0c;切勿商用。 另外四个是关于 Altera FPGA…

跨域和cookie

本文以前端的视角来探讨浏览器的跨域和cookie问题。 一、跨域 跨域简介&#xff1a; 为什么会出现跨域&#xff1f; 出于浏览器的同源策略限制&#xff0c;浏览器会拒绝跨域请求。 什么情况下出现跨域&#xff1f; 不同源就会跨域。同源即&#xff1a;协议、域名、端口号…

图文详解:箭头函数与常规函数的this指向问题

函数中this的指向问题特别容易让人迷糊&#xff0c;这里用示例来指点迷津&#xff0c;走出迷茫。 常规函数下的this指向 1. 纯粹的函数调用 function test(name) { console.log(name) console.log(this) } test(zjcopy) ; test.call(zjcopy, cuclife-2) ; test.call(fal…