超神之路 数据结构 2 —— Queue队列实现和循环队列和普通队列的性能比较

news2025/4/18 16:02:54

        接上一篇继续往下挖,在上一篇,我们实现了一个属于自己的动态数组。利用这个动态数组,我们来实现一个基于动态数组,一个属于自己的普通队列Queue。

        Queue 是一种它许我们从表的一段进行删除,表的另一端进行插入的线性表。可能也有人问啥叫线性表。

线性表:线性表是n个具有相同特性的数据元素的有限序列 (n>=0)。
n是线性表的长度,n=0就是空表,n>0时,表通常记作(a1,a2,a3...a_n)
除了a1外,表中每个元素 a[i] 均有唯一的前驱 a[i-1];
除了an外,表中每个元素 a[i] 均有唯一的后继 a[i+1];
线性表在逻辑上是线性结构,

        认识完了Queue,我们再来认识下Java util包下,Doug Lea 实现的Queue接口各个方法的定义。然后我们尽可能向JDK的源码靠近。

package java.util;

public interface Queue<E> extends Collection<E> {
    
    //添加一个元素,成功时返回true, 没有空间则报一个非法状态异常。
    boolean add(E e);
    //也时添加元素,比add更友好,add只给你异常,不给你false,offer给你返false。
    boolean offer(E e);

    
    //检索并删除此队列的头。此方法与poll的不同之处在于,它在队列为空时抛出异常。
    E remove();
    //检索并删除此队列的头,如果此队列为空则返回null。
    E poll();

    
    //检索但不删除此队列的头。此方法与peek的不同之处在于,它在队列为空时抛出异常。
    E element();
    //检索但不删除此队列的头,如果此队列为空则返回null。
    E peek();
}

        全部翻译了一遍,相信看出来了,add,offer都是添加元素,remove和poll也都是删除首元素,element和peek也都是查看首元素。唯一的区别是,前者给你异常,想停掉代码流;而后者给你null,让你自己处理。

        add,remove,element ===>均异常,offer,poll,peek ===> 均给null。

        这个接口还继承了 Collection 接口,Collection自然会对多种底层实现的集合容器实现一个统一的迭代器,迭代器这块1.8之后又加了一些实用defaut方法,这些就不关注了。主要关注下面2个方法:

 size方法,返回容器中元素个数,isEmpty方法判断是否为空。

        把以上抛异常的三个方法 add,remove,element 做个整合,我们推出以下接口,尽量来靠近JDK。

package com.company.queue;

/**
 * 队列
 *
 * @author <a href="610495444@qq.com">wang da wei</a>
 * 2021/11/7 0:24
 * @version 1.0.0
 */
public interface Queue<E> {

    /**
     * 入队
     * @param e
     */
    void add(E e);
    
    /**
     * 出队
     * @return 出队的元素
     */
    E remove();

    /**
     * 查看队首的元素。
     * @return 队首元素。
     */
    E element();

    /**
     * 是否是空的。
     * @return 是否为空。
     */
    boolean isEmpty();

    /**
     * 查看线性表中元素个数。
     * @return 元素个数
     */
    int size();
}

有了这种接口,我们就可以做2种实现了。先做一个普通的队列,再做一个循环队列。

基于上一篇的动态数组,我们很容易的就可以实现出一个FIFO的队列。代码如下:

package com.company.queue.common;

import com.company.array.DynamicArray;
import com.company.queue.Queue;

public class ArrayQueue<E> implements Queue<E> {

    private DynamicArray<E> dynamicArray ;

    public ArrayQueue() {
        this.dynamicArray = new DynamicArray<>();
    }

    @Override
    public void add(E e) {
        //添加到尾部 即 arr[size]位置。
        dynamicArray.add(e,dynamicArray.getSize());
    }

    @Override
    public E remove() {
        //移除头部元素,并返回。
        return dynamicArray.remove(0);
    }

    @Override
    public E element() {
        //查看第一个元素。
        return dynamicArray.get(0);
    }

    @Override
    public boolean isEmpty() {
        //查看是否为空
        return dynamicArray.getSize() == 0;
    }

    @Override
    public int size() {
        //获取动态数组中实际元素的个数。
        return dynamicArray.getSize();
    }

}

再来一个循环队列的实现:

package com.company.queue.loop;


import com.company.queue.Queue;

public class LoopQueue<E> implements Queue<E> {

    private E[] data;
    private int front, tail;
    private int size;  // 思考:不用 size 变量,如何实现?

    //如果用户知道最多为循环队列传递多少元素的时候,可以直接传入一个capacity.
    public LoopQueue(int capacity) {
        data = (E[]) new Object[capacity + 1];
        front =0;
        tail = 0;
        size = 0 ;
    }

    public LoopQueue(){
        this(10);
    }


    public int getCapacity(){
        return data.length -1;
    }

    @Override
    public void add(E e) {
        if( (tail +1 )% data.length  == front ){
            resize(getCapacity() * 2);
        }


        data[tail] =e;
        tail = (tail + 1) % data.length;
        size ++;
    }

    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity +1];

        for(int i = 0 ;i< size ;i++){
            int old_index = (i + front) % data.length;
            newData[i] = data[old_index];
        }

        data = newData;
        front = 0;
        tail =size;

    }

    @Override
    public E remove() {

        if(isEmpty() ) throw new IllegalArgumentException("cannot dequeue from an empty queue .");


        E ret = data[front];

        data[front] = null;

        front = (front + 1) % data.length;

        size--;

        return ret;
    }

    @Override
    public E element() {

        if(isEmpty() ) throw new IllegalArgumentException("cannot dequeue from an empty queue .");

        return data[front];
    }

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

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


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(String.format("Queue : size/capacity = %d/%d \nfront [ ",size,getCapacity()));
        for(int i = 0;i != tail; i = (i+1)% data.length){

            sb.append(data[i]);

            //当前索引不是最后一个元素。
            if((i+1)%data.length != tail ){
                sb.append(" , ");
            }
        }
        sb.append(" ] tail ");
        return sb.toString();
    }


    public static void main(String[] args) {
        LoopQueue<Integer> queue  = new LoopQueue<>();
        for(int i = 0 ;i<20;i++){
            queue.add(i);
            System.out.println(queue);
            if(i%3 == 2){
                queue.remove();
                System.out.println(queue);
            }
        }
    }
}

两种队列的性能比较:

package com.company.queue;

import com.company.queue.common.ArrayQueue;
import com.company.queue.loop.LoopQueue;

import java.util.Random;

public class MainQueueTest {

    public static void main(String[] args) {
        int opCount = 10_0000;

        ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
        double arrayQueueTime = testQueue(arrayQueue, opCount);
        System.out.println(arrayQueueTime);


        LoopQueue<Integer> loopQueue = new LoopQueue<>();
        double loopQueueTime = testQueue(loopQueue, opCount);
        System.out.println(loopQueueTime);


    }


    public static double testQueue(Queue queue,int opCount){

        long start = System.nanoTime();

        Random random = new Random();
        for(int i = 0 ;i<opCount;i++){
            queue.add(random.nextInt(Integer.MAX_VALUE));
        }
        for(int i = 0 ;i<opCount;i++){
            queue.remove();
        }

        long end = System.nanoTime();

        return (end- start)/10_0000_0000D;  //纳秒 : 9个0. 不要忘了加D,或者 后缀 .0

    }
}

        由于循环队列出队操作是O(1)的时间复杂度,所以当数据量越大的时候,一般有更好的性能优势。

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

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

相关文章

Docker—苹果Mac安装Docker的两种方式

文章目录Docker简介方式一&#xff1a;官网dmg安装方式二&#xff1a;homebrew安装前置条件brew安装docker关注微信公众号&#xff1a;CodingTechWork&#xff0c;一起学习进步。Docker简介 &emsp docker就是集镜像、容器和仓库三个概念于一身的集装箱&#xff0c;应用于…

GPO:组策略与系统配置

1、所有的服务器不需要按ctrl+alt+del交互式登录; 2、关闭所有机器的睡眠功能 3、所有计算机都不允许登录Microsoft账户; 4、允许IT组进行系统时间修改 5、设定所有职务为managers的用户为本地管理员,除了域控制器 GPO:组策略与系统配置 所有的服务器不需要按ctrl+alt+del;…

基于ELK搭建的本地社工库

简介 ELK原本是一个开源实时日志分析平台。 ELK是三个开源软件的缩写&#xff0c;分别为&#xff1a;Elasticsearch 、 Logstash以及Kibana 。目前又新增了一个Beats&#xff0c;是一个轻量级的日志收集处理工具&#xff0c;Beats占用资源少&#xff0c;适合于在各个服务器上…

Greenplum GPKafka【实践 01】使用GPKafka实现Kafka数据导入Greenplum数据库踩坑问题记录(不断更新ing)

1.说明 Kafka 是分布式消息订阅系统&#xff0c;有非常好的横向扩展性&#xff0c;可实时存储海量数据&#xff0c;是很常用的流数据处理中间件。物联网设备采集的数据很多时候就是通过 Kafka 进行处理的。当 Kafka 数据要入库分布式数据库 Greenplum 时&#xff0c;我们就需要…

二、IAR新建一个工程

之前录制了无线传感网综合项目实战课程&#xff0c;这个课程非常适合应届毕业生和想转行Linux的朋友&#xff0c;用来增加项目经验。 其中一部分内容是关于CC2530zigbee的知识&#xff0c;后面会更新几篇关于cc2530的文章&#xff0c;敬请关注&#xff01; 下面是该系列文章链…

gcc 好玩的 builtin 函数

gcc 好玩的 builtin 函数 前言 在本篇文章当中主要想给大家介绍一些在 gcc 编译器当中给我们提供的一些好玩的内嵌函数 (builtin function)&#x1f923;&#x1f923;&#x1f923; 。 __builtin_frame_address 使用内嵌函数实现 __builtin_frame_address(x) // 其中 x 一…

【GPU】Nvidia CUDA 编程高级教程——利用蒙特卡罗法求解近似值(MPI方法)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

无线蓝牙运动耳机推荐,目前实用性不错的运动耳机推荐

对于运动人士来说&#xff0c;运动耳机是日常出门标配&#xff0c;一款实用性好的运动耳机能给我们带来很大的动力&#xff0c;在运动时戴着听音乐&#xff0c;能够释放压力的同时让运动过程变得更加心情愉快。但是运动耳机实在是太多了&#xff0c;琳琅满目&#xff0c;各种各…

c++11特性(3)

1.lambda表达式 a.出现由来&#xff1a; 以sort为例&#xff0c;我们可以传入一个仿函数对自定义类型进行排序。但是&#xff0c;对每一种比较方式我们都要显示传一个仿函数太麻烦。要是乱命名更加要命&#xff0c;那就除了写这段代码的人没人看得懂了&#xff01; b.lambda…

基于象虫损害优化算法的投资组合问题(Matlab代码实现)

&#x1f4dd;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;…

我的写作心得

前言 首先&#xff0c;博主是一个在校高中生&#xff0c;平常可以说几乎没有时间来创作文章&#xff0c;所以说断更也是很正常的 时隔半年&#xff0c;我也在csdn累计了400多个粉丝&#xff08;截止我创作这篇文章的时候&#xff09;&#xff0c;从之前的一无所有&#xff0c…

PCL交互选择ROI区域

PCL的配置和如何配准点云可见博主之前的博客 win10环境下PCL安装和配置回顾&#xff08;一&#xff09;_竹叶青lvye的博客-CSDN博客_pcl win10 win10环境下PCL安装和配置回顾&#xff08;二&#xff09;_竹叶青lvye的博客-CSDN博客_win10 安装pcl PCL - 3D点云配准(registra…

状态保持-JWT

“ Web的状态保持技术 -JWT&#xff08;Json Web Token&#xff09;” 在分布式微服务技术流行的今天&#xff0c;大型网站对JWT的使用愈加频繁&#xff0c;相比较于传统的session cookie。 HTTP 是一个无状态的协议&#xff0c;何为无状态呢&#xff1f;就是说这本次请求和上次…

【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用(仿真、原理图、源代码、低重复参考文档、答辩PPT、英文文献)

【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用&#xff08;仿真、原理图、源代码、低重复参考文档、答辩PPT、英文文献&#xff09; 文章目录【毕业设计】67-基于单片机的三相电压型SPWM逆变器控制设计及应用&#xff08;仿真、原理图、源代码、低重复参考文…

网站文章采集器-万能文章采集器

随着时代的发展。互联网无时不刻地出现在我们的生活中&#xff0c;大家也越来越注重效率&#xff0c;今天小编就给大家来分享一款网站文章采集器。只需要点几下鼠标就能轻松获取数据&#xff0c;不管是导出本地还是发布到网上。网站人员可以实现自动采集&#xff0c;定时发布&a…

[计算机毕业设计]食品安全数据的关联分析模型的应用

前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着准备考研,考公,考教资或者实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过…

idea里面完整创建maven项目(包含如何使用)

目录 前言&#xff1a; 1.Maven和Maven Archetype区别&#xff1f; 2.创建maven项目之前的步骤&#xff08;必看&#xff09; 一、创建maven 1.打开idea--->文件--->新建--->项目 2.新建项目--->自定义项目名称--->选择Maven-->创建 3. 创建成功 二、…

微信小程序连接蓝牙汉印HM-A300L标签打印机

需求&#xff1a; 参考文章&#xff1a; 微信小程序实现蓝牙打印 打印机CPCL编程参考手册&#xff08;CPCL 语言&#xff09; 蓝牙打印机CPCL编程手册~汉印HM-A300 无用小知识&#xff1a; A300系列&#xff1a;先将打印机关机然后装好纸&#xff0c;同时按住屏幕左右两边的按…

UDS诊断网络层ISO15765-2(CAN)

诊断协议那些事儿 本文为诊断协议那些事儿专栏文章&#xff0c;从一个 ECU 到另一个 ECU&#xff0c;或外部诊断设备和一个 ECU 之间的通信&#xff0c;不仅依赖前文介绍的诊断寻址方式&#xff0c;更需要关注数据的传输&#xff0c;依赖网络层、传输层协议去完成&#xff0c;…

[附源码]java毕业设计社团管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…