用环形数组实现队列(多种高级方法,由浅入深)

同普通数组实现的队列相比,普通数组的头结点和尾节点都是固定的,在进行移除的时候如果移除了一个节点,后面所有节点都需要进行移除操作,需要的时间复杂度更高
在环形数组中,确定了头尾指针的环形数组很好地解决了这一点
我们来看具体思路
方法一:牺牲一个存储容量

有两个指针,在环形数组为空的时候它们都指向这个0索引

加入元素之后,尾指针后移一位,直到尾指针的 下一个和头指针重合,如图

在进行移除操作的时候,将头指针前进一位即可
isfull:当尾指针的下一个位置与头指针重合时,说明环形数组已经满了
用索引来表示的话每次让移动让tail+1,和数组长度取模即可,再和head的值进行判断
isempty:当尾指针与头指针重合的时候,说明环形数组为空
为了使下标可以为0到4循环,我们需要控制tail和head,所以我们可以这么表示
进行head和tail的迭代
head=(head+1)%array.length;tail=(tail+1)%array.length;
在迭代器中,将初始节点定位为p=head,循环条件为p!=tail,因为tail指的是下一个存入元素的数组位置
我们来看方法实现
public class ArrayQueue1 <E>implements Queue<E>,Iterable<E>{private E[] array;private int head=0;private int tail=0;@SuppressWarnings("all")public ArrayQueue1(int capacity) {array = (E[]) new Object[capacity+1];}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int p=head;@Overridepublic boolean hasNext() {return p!=tail;}@Overridepublic E next() {E value=array[p];p=(p+1)%array.length;return value;}};}@Overridepublic boolean offer(E value) {if(isFull()){return false;}array[tail]=value;tail=(tail+1)%array.length;return true;}@Overridepublic E poll() {if(isEmpty()){return null;}E value=array[head];head=(head+1)%array.length;return value;}@Overridepublic E peek() {if(isEmpty()){return null;}return array[head];}@Overridepublic boolean isEmpty() {return head==tail;}@Overridepublic boolean isFull() {return (tail+1)%array.length==head;}
}
方法二:通过数组内元素个数进行判断
和之前的思路相近,我们多设置一个size记录元素个数,这个时候不需要牺牲一个存储容量,在isfull判断中直接通过比较size和数组长度,在非空判断中,判断size是否为0即可
关于迭代器遍历,我们需要再引入一个变量count,初始值赋值为0,循环条件是count<size,因为size代表元素个数
我们来看代码
ic class ArrayQueue2 <E>implements Queue<E>,Iterable<E>{private E[] array;private int head=0;private int tail=0;private int size=0;//队列中元素个数@SuppressWarnings("all")public ArrayQueue2(int capacity) {array = (E[]) new Object[capacity];}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int p=head;int count=0;@Overridepublic boolean hasNext() {return count<size;}@Overridepublic E next() {E value=array[p];p=(p+1)%array.length;count++;return value;}};}@Overridepublic boolean offer(E value) {if(isFull()){return false;}array[tail]=value;tail=(tail+1)%array.length;size++;return true;}@Overridepublic E poll() {if(isEmpty()){return null;}E value=array[head];head=(head+1)%array.length;size--;return value;}@Overridepublic E peek() {if(isEmpty()){return null;}return array[head];}@Overridepublic boolean isEmpty() {return size==0;}@Overridepublic boolean isFull() {return size==array.length;}
}
方法三:不通过牺牲元素和进行headtail处理

我们还是设置头尾指针,我们以上图为例子,有一个长度为3的环形数组
我们一直将head和tail进行移动的时候递增,不再通过head和tail做处理来代表索引,而是通过将递增的head和tail进行计算来求索引,我们不再通过array[head]或者array[tail]代表元素值,而是通过
array[head%array.length]和array[tail%array.length]
在进行非空判断的时候,思路还是tail=head
在进行非满判断的时候,思路则是通过tail-head等不等于数组长度,无需牺牲一个存储空间。
我们来看代码
public class ArrayQueue3<E> implements Queue<E>, Iterable<E>{private final E[] array;private int head = 0;private int tail = 0;@SuppressWarnings("all")public ArrayQueue3(int capacity) {array = (E[]) new Object[capacity];}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int p=head;@Overridepublic boolean hasNext() {return p!=tail;}@Overridepublic E next() {E value=array[head%array.length];p++;return value;}};}@Overridepublic boolean offer(E value) {if(isFull()){return false;}array[tail%array.length] = value;tail++;return true;}@Overridepublic E poll() {if (isEmpty()){return null;}E value=array[head%array.length];head++;return value;}@Overridepublic E peek() {if (isEmpty()){return null;}E vakue=array[head%array.length];return vakue;}@Overridepublic boolean isEmpty() {return head == tail;}@Overridepublic boolean isFull() {return tail-head == array.length;}
}
一些问题思考
在方法三中,我们不对head和tail进行处理,而是放任其递增,因为int是有范围的,再超过tail的最大范围之后会报错,所以我们思考处理方法
第一种方法,通过转成长整型long避免越界报错,强制转型
array[(int) (Integer.toUnsignedLong(tail)%array.length)]
但是所有涉及的数组都需要进行强制转型,会比较复杂
二进制中按位与运算
求模运算:
- 如果除数是2的n次方
- 那么被除数的后n位即为余数(模)
- 求被除数的后n位方法:与2^n-1次方进行按位与运算
array[head&(array.length-1)];
修改如上
如果数组长度不是2的n次方,我们该如何应对呢
将数组长度根据传入参数设置成2的n次方
第一种方法直接抛出异常
2^n&2^n-1=0
我们可以根据这个规律直接抛出异常
public ArrayQueue3(int capacity) {//1.抛异常if((capacity&capacity-1)!=0){throw new IllegalArgumentException("capacity must be a power of 2");}array = (E[]) new Object[capacity];}
第二种方法是修改传入参数
我们先看思路,假设传进来的数值是30

我们需要将30改成32,也就是找到最近的2的n次方等于32,
我们可以看到log2(30)大约是等于4.几,为了使其变成5,我们可以将log(30)+1,并将其强制转型为整型
又因为idea java语言中的Math包里面只有log10,所以我们可以使用换底公式
但是如果说本来这个数字就是2的n次幂的话再+1就会修改它的值,所以我们可以给传入的参数值进行-1处理,这样就可以很好的避免
在我们得到了n次幂的基础上,我们只需要对1进行左移即可(2^0=1)
代码如下
public static void main(String[] args) {int c=30;int n=(int)(Math.log10(c-1)/Math.log10(2))+1;System.out.println(1<<n);}
或者我们也可以直接利用一个公式
求离C最近,比C大的2^n次幂
c-=1;
c|=c>>1;
c|=c>>2;
c|=c>>4;
c|=c>>8;
c|=c>>16;
c+=1;
代码如下
@SuppressWarnings("all")public ArrayQueue3(int c) {c-=1;c|=c>>1;c|=c>>2;c|=c>>4;c|=c>>8;c|=c>>16;c+=1;array = (E[]) new Object[c];}
相关文章:
用环形数组实现队列(多种高级方法,由浅入深)
同普通数组实现的队列相比,普通数组的头结点和尾节点都是固定的,在进行移除的时候如果移除了一个节点,后面所有节点都需要进行移除操作,需要的时间复杂度更高 在环形数组中,确定了头尾指针的环形数组很好地解决了这一…...
springboot框架使用RabbitMQ举例代码
以前分享过一个理论有兴趣的小伙伴可以看下 https://blog.csdn.net/Drug_/article/details/138164180 不多说 还是直接上代码 第一步:引入依赖 可以不指定版本 <!-- amqp --><dependency><groupId>org.springframework.boot</groupId…...
Java实现一个延时队列
文章目录 前言正文一、基本概念1.1 延时队列的特点1.2 常见的实现方式 二、Java原生的内存型延时队列2.1 定义延时元素DelayedElement2.2 定义延时队列管理器DelayedQueueManager2.3 消费元素2.4 调试2.5 调试结果2.6 精髓之 DelayQueue.poll() 三、基于Redisson的延时队列3.1 …...
为什么说vue是双向数据流
Vue.js 被称为 双向数据绑定(two-way data binding),是因为它支持数据在 视图(View) 和 模型(Model) 之间双向流动。这意味着,当 数据变化 时,视图会自动更新;…...
创造属于你的 Claude Prompt 和个性化 SVG 卡片|对李继刚老师提示词的浅浅解析与总结
❤️ 如果你也关注大模型与 AI 的发展现状,且对大模型应用开发非常感兴趣,我会快速跟你分享最新的感兴趣的 AI 应用和热点信息,也会不定期分享自己的想法和开源实例,欢迎关注我哦! 🥦 微信公众号ÿ…...
redis与本地缓存
本地缓存是将数据存储在应用程序所在的本地内存中的缓存方式。既然,已经有了 Redis 可以实现分布式缓存了,为什么还需要本地缓存呢?接下来,我们一起来看。 为什么需要本地缓存? 尽管已经有 Redis 缓存了,但…...
git撤销commit和add
撤销commit git reset --soft HEAD^撤销add git reset .查看状态 git status...
【361】基于springboot的招生宣传管理系统
摘 要 使用旧方法对招生宣传管理系统的信息进行系统化管理已经不再让人们信赖了,把现在的网络信息技术运用在招生宣传管理系统的管理上面可以解决许多信息管理上面的难题,比如处理数据时间很长,数据存在错误不能及时纠正等问题。这次开发的招…...
【一些关于Python的信息和帮助】
Python是一种广泛使用的高级编程语言,它的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而不是使用大括号或关键字)。Python支持多种编程范式,包括面向对象、命令式、函数式和过程式编程。 以…...
creo toolkit二次开发学习之程序集(ProAsmcomp)和装配体组件路径对象(ProAsmcomppath)
程序集ProAsmcomp可以理解为装配体组件对象。 对象ProAssembly是ProSolid的一个实例,并共享相同的声明。因此,ProAssembly对象可以作为适用于装配体的任何ProSolid和ProMdl函数的输入。特别是,因为你可以使用函数ProSolidFeatVisit()来遍历特…...
深入浅出 Spring Boot 与 Shiro:构建安全认证与权限管理框架
一、Shiro框架概念 (一)Shiro框架概念 1.概念: Shiro是apache旗下一个开源安全框架,它对软件系统中的安全认证相关功能进行了封装,实现了用户身份认证,权限授权、加密、会话管理等功能,组成一…...
外包干了三年,精神严重内耗...
前段时间我同事(做测试的一个妹子)跟我讲,感觉早上起来十分的疲惫,不想上班,问我们这是什么样的现象,其实有时候我也有这种感觉,虽然我卷,但我也是肉体凡胎啊!不是机器人…...
ruoyi-vue集成tianai-captcha验证码
后端代码 官方使用demo文档:http://doc.captcha.tianai.cloud/#%E4%BD%BF%E7%94%A8demo 我的完整代码:https://gitee.com/Min-Duck/RuoYi-Vue.git 主pom.xml 加入依赖 <!-- 滑块验证码 --><dependency><groupId>cloud.tianai.captc…...
Django安装
在终端创建django项目 1.查看自己的python版本 输入对应自己本机python的版本,列如我的是3.11.8 先再全局安装django依赖包 2.在控制窗口输入安装命令: pip3.11 install django 看到Successflully 说明我们就安装成功了 python的Scripts文件用于存…...
Ubuntu 20.04 安装 QGC v4.3 开发环境
Ubuntu 20.04 安装 QGC开发环境 1. 准备安装 Qt 5.15.2安装依赖获取源码 2. 编译参考 前言 QGC ( QGroundControl) 是一个开源地面站,基于QT开发的,有跨平台的功能。可以在Windows,Android,MacOS或Linux上运行。它可以将PX4固件加…...
WPF+MVVM案例实战(二十一)- 制作一个侧边弹窗栏(AB类)
文章目录 1、案例效果1、侧边栏分类2、AB类侧边弹窗实现1.文件创建2、样式代码与功能代码实现3、功能代码实现 3 运行效果4、源代码获取 1、案例效果 1、侧边栏分类 A类 :左侧弹出侧边栏B类 :右侧弹出侧边栏C类 :顶部弹出侧边栏D类 …...
linux中怎样登录mysql数据库
在Linux中登录MySQL数据库,可以使用以下命令: mysql -u username -p 其中,username是你的MySQL用户名。运行该命令后,系统会提示你输入密码。 如果MySQL服务器不在本地主机或者你需要指定不同的端口,可以使用以下命…...
深入理解 Linux 内存管理:free 命令详解
在 Linux 系统中,内存是关键的资源之一,管理和监控内存的使用情况对系统的稳定性和性能至关重要。free 命令是 Linux 中用于查看内存使用情况的重要工具,它可以让我们快速了解系统中物理内存和交换分区(Swap)的使用状态…...
指针万字超级最强i解析与总结!!!!!
文章目录 1.内存和地址1.1内存1.2究竟该如何理解编址 2.指针变量和地址2.1 取地址操作符(&)2.2指针变量和解引用操作符(*)2.2.1指针变量2.2.2如何拆解指针类型2.2.3解引用操作符 2.3 指针变量的大小 3.指针变量类型的意义3.1指…...
告别生硬电子音,这款TTS软件让语音转换更自然动听
Balabolka是一款革新性的文本语音转换工具,为用户提供了极其灵活和个性化的阅读体验。这款软件不仅仅是简单的文字朗读器,更是一个智能的语音助手,能够将各类文本瞬间转化为生动自然的语音输出。 软件的核心优势在于其卓越的文件兼容性和多样…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
