数据结构与算法【队列】的Java实现

news2024/11/27 17:59:39

队列:以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头。

通用接口

public interface Queue<E> {
    /**
     * 插入队列
     */
    boolean offer(E value);
    /**
     * 从队列中获取值并移除
     */
    E poll();
    /**
     * 从队列中获取值但不移除
     */
    E peek();
    /**
     * 检查队列是否已满
     */
    boolean isFull();
    /**
     * 检查队列是否不为空
     */
    boolean isEmpty();
}

基于单向循环链表的简单实现

public class LinkedQueue<E> implements Queue<E>, Iterable<E> {
    //提供哨兵节点
    private Node<E> sentinel = new Node<E>(null, null);
    //提供尾节点
    private Node<E> tail = sentinel;
    //队列大小
    private int size = 0;
    //队列容量
    private int capacity = Integer.MAX_VALUE;

    public LinkedQueue(int capacity) {
        this.capacity = capacity;
        tail.next = sentinel;
    }

    private static class Node<E> {
        Node<E> next;
        E value;

        public Node(Node<E> next, E value) {
            this.next = next;
            this.value = value;
        }
    }

    @Override
    public boolean offer(E value) {
        //在队尾插入元素,选择尾插法
        if (isFull()) {
            return false;
        }
        Node<E> node = new Node<>(sentinel, value);
        tail.next = node;
        tail = node;
        size++;
        return true;
    }


    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        Node<E> first = sentinel.next;
        if (first==tail){
            //如果是最后一个节点,那么将tail指向sentinel
            tail =sentinel;
        }
        sentinel.next = first.next;
        E value = first.value;
        size--;
        return value;
    }

    @Override
    public E peek() {
        return sentinel.next.value;
    }

    @Override
    public boolean isFull() {
        return size == capacity;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            Node<E> p = sentinel.next;

            @Override
            public boolean hasNext() {
                return p != sentinel;
            }

            @Override
            public E next() {
                E value = p.value;
                p = p.next;
                return value;
            }
        };
    }
}

基于循环数组的简单实现

实现前,介绍一下环形数组与数组的区别

  • 对比普通数组,起点和终点更为自由,不用考虑数据移动(普通数组移除元素时需要移动其他元素)
  • “环”意味着不会存在【越界】问题
  • 数组性能更佳
  • 环形数组比较适合实现有界队列、RingBuffer 等
public class ArraysQueue<E> implements Queue<E>, Iterable<E> {
    private int head = 0;
    private int tail = 0;
    //用来记录循环数组大小
    private final int length;
    private E[] array;

    @SuppressWarnings("all")
    public ArraysQueue(int capacity) {
        this.length = capacity + 1;
        //加一是为尾指针留一个空间去判断是否队列已满
        this.array = (E[]) new Object[length];
    }

    @Override
    public boolean offer(E value) {
        if (isFull()) {
            return false;
        }
        array[tail++] = value;
        tail = tail % length;
        return true;
    }

    @Override
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        E value = array[head];
        head = (head + 1) % length;
        return value;
    }

    @Override
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return array[head];
    }

    @Override
    public boolean isFull() {
        if ((tail + 1) % length == head) {
            return true;
        }
        return false;
    }

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

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int p = head;

            @Override
            public boolean hasNext() {
                return p != tail;
            }

            @Override
            public E next() {
                E value = array[p];
                p = (p + 1) % length;
                return value;
            }
        };
    }
}

在Java源码中的基于数组实现的队列对容量有一个要求,即一定是2的n次方。之所以这么要求,是因为方便头指针和尾指针的边界确认。

我们的实现方式中指针的值是通过+1并取余来确定指针的下一个位置,也就是说,head和tail的值始终是在数组长度中。而在Java源码中,并没有规定head与tail的取值一定是数组长度内,而是不停的+1然后通过对数组长度的取余,来确定head与tail的下标位置。

但是这样又存在一个问题。那就是head或是tail超过了int类型所能表达的最大值后,再去取余会得到负数,使用负数去数组中拿元素会报错。为了解决这个问题,Java针对二进制特点采用了更高效率的实现方案。

首先就是规定数组长度一定是2的n次方。

看下面例子

因此我们不需要在意符号位是否为负数,只需要关心余数的二进制即可。

对于如何通过二进制的方式获取到余数。是二进制的另一个特性

我们查看ArrayDeque源码中的添加元素方法

正是采用了二进制的位运算特性来控制head与tail在数组中的下标位置。如果用户指定数组队列不是一个2的n次方时,他会强制扩容到最近的2的n次方大小。具体实现方式如下

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

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

相关文章

Web浏览器的同源策略(same-origin policy)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

卷积神经网络(CNN)mnist手写数字分类识别的实现

文章目录 前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;我的环境&#xff1a; 2. 导入数据3.归一化4.可视化5.调整图片格式 二、构建CNN网络模型三、编译模型四、训练模型五、预测六、知识点详解1. MNIST手写数字数据集介绍2. 神经网络程序说明3. 网…

6.docker运行mysql容器-理解容器数据卷

运行mysql容器-理解容器数据卷 1.什么是容器数据卷2.如何使用容器数据卷2.1 数据卷挂载命令2.2 容器数据卷的继承2.3 数据卷的读写权限2.4 容器数据卷的小实验&#xff08;加深理解&#xff09;2.4.1 启动挂载数据卷的centos容器2.4.2 启动后&#xff0c;在宿主机的data目录下会…

IDEA创建SpringBoot的多模块项目教程

最近在写一个多模块的SpringBoot项目&#xff0c;基于过程总了一些总结&#xff0c;故把SpringBoot多个模块的项目创建记录下来。 首先&#xff0c;先建立一个父工程&#xff1a; &#xff08;1&#xff09;在IDEA工具栏选择File->New->Project &#xff08;2&#xff0…

热点检测/降级框架Akali的部分原理解析

发现个“轻量级本地化热点检测/降级框架 这个框架名为Akali,项目地址&#xff1a;https://gitee.com/bryan31/Akali主要有两个作用 1&#xff1a;热点检测及处理 2&#xff1a;降级检测及处理 从官网文档来看使用是比较简单的&#xff0c;一个注解就能搞定 怀着好奇的心情c…

Echarts 实现两两柱图重叠(背景和实际值柱图)

Echarts实现两两重叠柱状图_echarts 重叠柱状图_Web_阿凯的博客-CSDN博客 引用启发的博客 先来效果&#xff1a; option {backgroundColor: #03213D,animation: true, // 控制动画是否开启animationDuration: 1000, // 动画的时长, 它是以毫秒为单位animationDuration: func…

数据结构C语言之线性表

发现更多计算机知识&#xff0c;欢迎访问Cr不是铬的个人网站 1.1线性表的定义 线性表是具有相同特性的数据元素的一个有限序列 对应的逻辑结构图形&#xff1a; 从线性表的定义中可以看出它的特性&#xff1a; &#xff08;1&#xff09;有穷性&#xff1a;一个线性表中的元…

Homography详解在MVSNet中的应用

Homography&#xff08;单应性变换&#xff09;是计算机视觉中的一个重要概念&#xff0c;用于描述两个平面之间的透视关系。在图像处理和计算机视觉中&#xff0c;Homography通常表示两个平面之间的投影关系&#xff0c;这种关系可以通过一个3x3的矩阵来表示。 在数学上&…

YB4019是一款完整的单电池锂离子恒流/恒压线性充电器电池

YB4019 耐压18V 1A线性双节8.4V 锂电充电芯片 概述&#xff1a; YB4019是一款完整的单电池锂离子恒流/恒压线性充电器电池。底部采用热增强ESOP8封装&#xff0c;外部组件数量低使YB4019成为便携式应用的理想选择。此外&#xff0c;YB4019设计用于在USB电源规格范围内工作。Y…

洗袜子的洗衣机哪款好?内衣洗衣机测评

随着人们的生活水平的提升&#xff0c;越来越多小伙伴来开始追求更高的生活水平&#xff0c;一些智能化的小家电就被发明出来&#xff0c;而且内衣洗衣机是其中一个。现在通过内衣裤感染到细菌真的是越来越多&#xff0c;所以我们对内衣裤的清洗频次会高于普通衣服&#xff0c;…

File类和IO流

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

2024苹果笔记本清理内存清理优化工具CleanMyMac X

在使用苹果笔记本电脑的过程中&#xff0c;清理内存是保持电脑运行流畅的重要步骤之一。当我们使用大量的应用程序和文件时&#xff0c;电脑的内存可能会被占满&#xff0c;导致系统变慢甚至出现崩溃的情况。因此&#xff0c;了解如何清理苹果笔记本的内存是非常必要的。本文将…

代码随想录 Day47 动态规划15 LeetCode T583 两个字符串的删除操作 T72 编辑距离

LeetCode T583 两个字符串的删除操作 题目链接:583. 两个字符串的删除操作 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 本题有两个思路 1.使用两个字符串的长度之和-2*最长公共子串(换汤不换药) 代码随想录Day45 动态规划13 LeetCode T1143最长公共子序列 T1135 不相交…

适用于 Mac 的 10 款最佳数据恢复工具

对于依赖计算机处理重要文件&#xff08;无论是个人照片还是重要业务文档&#xff09;的任何人来说&#xff0c;数据丢失都可能是一场噩梦。 值得庆幸的是&#xff0c;有多种数据恢复工具专门用于Mac用户&#xff0c;可以帮助您恢复丢失或意外删除的文件。 在本文中&#xff0c…

Rust图形界面:eGUI的Panel布局

文章目录 Panel布局尺寸调节源码 Panel布局 eGUI提供面板堆叠的布局方案&#xff0c;即Panel布局。其布局逻辑是&#xff0c;根据当前面板指定的方向&#xff0c;尽可能地填充空间。 CentralPanel 占据屏幕剩余部分的空间SidePanel 占据屏幕两侧的空间&#xff0c;在具体调用…

【网络】TCP协议的相关实验

TCP协议的相关实验 一、理解listen的第二个参数1、实验现象2、TCP 半连接队列和全连接队列3、关于listen的第二个参数的一些问题4、SYN洪水Ⅰ、什么是SYN洪水攻击Ⅱ、如何解决SYN洪水攻击&#xff1f; 二、使用Wireshark分析TCP通信流程 一、理解listen的第二个参数 在编写TCP…

【23真题】无耻!“官方”假真题!害人!

这套华侨23真题是学弟给我从考场抄出来的版本&#xff0c;我刚刚做完解析&#xff01;后台就收到了另外一份“官方华侨23真题”的投稿。我本想对对回忆版&#xff0c;补充下题干。结果一对吓一跳&#xff01;竟然一道题都不一样&#xff01;给大家看下&#xff0c;真的好逼真&a…

《向量数据库指南》——TruLens + Milvus Cloud 构建RAG案例

具体案例 如前所述,RAG 配置选择可能对消除幻觉产生重大影响。下文中将基于城市百科文章构建问答 RAG 应用并展示不同的配置选择是如何影响应用性能的。在搭建过程中,我们使用 LlamaIndex 作为该应用的框架。大家可以在 Google Colab( https://colab.research.google.com/git…

Theory behind GAN

假如要生成一些人脸图&#xff0c;实际上就是想要找到一个分布&#xff0c;从这个分布内sample出来的图片像是人脸&#xff0c;分布之外生成的就不像人脸。而GAN要做的就是找到这个distribution。 在GAN之前用的是Maximum Likelihood Estimation。 Maximum Likelihood Estimat…