前情回顾

在上一篇,笔者给大家介绍了数组队列,并且在文末提出了数组队列实现上的劣势,以及带来的性能问题(因为数组队列,在出队的时候,我们往往要将数组中的元素往前挪动一个位置,这个动作的时间复杂度O(n)级别),如果不清楚的小伙伴欢迎查看阅读。为了方便大家查阅,笔者在这里贴出相关的地址:

为了解决数组队列带来的问题,本篇给大家介绍一下循环队列。

思路分析图解

啰嗦一下,由于笔者不太会弄贴出来的图片带有动画效果,比如元素的移动或者删除(毕竟这样看大家比较直观),笔者在这里只能通过静态图片的方式,帮助大家理解实现原理,希望大家不要见怪,如果有朋友知道如何搞的话,欢迎在评论区慧言。

在这里,我们声明了一个容量大小为8的数组,并标出了索引0-7,然后使用front和tail分别来表示队列的,队首和队尾;在下图中,front和tail的位置一开始都指向是了索引0的位置,这意味着当front == tai的时候 队列为空 大家务必牢记这一点,以便区分后面介绍队列快满时的临界条件

为了大家更好地理解下面的内容,在这里,我简单做几点说明

front:表示队列队首,始终指向队列中的第一个元素(当队列空时,front指向索引为0的位置)

tail:表示队列队尾,始终指向队列中的最后一个元素的下一个位置

元素入队,维护tail的位置,进行tail++操作

元素出队,维护front的位置,进行front++操作

上面所说的,元素进行入队和出队操作,都简单的进行++操作,来维护tail和front的位置,其实是不严谨的,正确的维护tail的位置应该是(tail + 1) % capacity,同理front的位置应该是(front + 1) % capacity,这也是为什么叫做循环队列的原因,大家先在这里知道下,暂时不理解也没关系,后面相信大家会知晓。

下面我们看一下,现在如果有一个元素a入队,现在的示意图:

我们现在看到了元素a入队,我们的tail指向的位置发生了变化,进行了++操作,而front的位置,没有发生改变,仍旧指向索引为0的位置,还记得笔者上面所说的,front的位置,始终指向队列中的第一个元素,tail的位置,始终指向队列中的最后一个元素的下一个位置

现在,我们再来几个元素b、c、d、e进行入队操作,看一下此时的示意图:

想必大家都能知晓示意图是这样,好像没什么太多的变化(还请大家别着急,笔者这也是方便大家理解到底是什么循环队列,还请大家原谅我O(∩_∩)O哈!)

看完了元素的入队的操作情况,那现在我们看一下,元素的出队操作是什么样的?

元素a出队,示意图如下:

现在元素a已经出队,front的位置指向了索引为1的位置,现在数组中所有的元素不再需要往前挪动一个位置

这一点和我们的数组队列(我们的数组队列需要元素出队,后面的元素都要往前挪动一个位置)完全不同,我们只需要改变一下front的指向就可以了,由之前的O(n)操作,变成了O(1)的操作

我们再次进行元素b出队,示意图如下:

到这里,可能有的小伙伴会问,为什么叫做,循环队列?那么现在我们尝试一下,我们让元素f、g分别进行入队操作,此时的示意图如下:

大家目测看下来还是没什么变化,如果此时,我们再让一个元素h元素进行入队操作,那么问题来了我们的tail的位置该如何指向呢?示意图如下:

根据我们之前说的,元素入队:维护tail的位置,进行tail++操作,而此时我们的tail已经指向了索引为7的位置,如果我们此时对tail进行++操作,显然不可能(数组越界)

细心的小伙伴,会发现此时我们的队列并没有满,还剩两个位置(这是因为我们元素出队后,当前的空间,没有被后面的元素挤掉),大家可以把我们的数组想象成一个环状,那么索引7之后的位置就是索引0

如何才能从索引7的位置计算到索引0的位置,之前我们一直说进行tail++操作,笔者也在开头指出了,这是不严谨的,应该的是(tail + 1) % capacity这样就变成了(7 + 1) % 8等于 0

所以此时如果让元素h入队,那么我们的tail就指向了索引为0的位置,示意图如下:

假设现在又有新的元素k入队了,那么tail的位置等于(tail + 1) % capacity 也就是(0 + 1)% 8等于1就指向了索引为1的位置

那么问题来了,我们的循环队列还能不能在进行元素入队呢?我们来分析一下,从图中显示,我们还有一个索引为0的空的空间位置,也就是此时tail指向的位置

按照之前的逻辑,假设现在能放入一个新元素,我们的tail进行(tail +1) % capacity计算结果为2(如果元素成功入队,此时队列已经满了),此时我们会发现表示队首的front也指向了索引为2的位置

如果新元素成功入队的话,我们的tail也等于2,那么此时就成了 tail == front ,一开始我们提到过,当队列为空的tail == front,现在呢,如果队列为满时tail也等于front,那么我们就无法区分,队列为满时和队列为空时收的情况了

所以,在循环队列中,我们总是浪费一个空间,来区分队列为满时和队列为空时的情况,也就是当 ( tail + 1 ) % capacity == front的时候,表示队列已经满了,当front == tail的时候,表示队列为空。

了解了循环队列的实现原理之后,下面我们用代码实现一下。

代码实现

接口定义 :Queue

public interface Queue {

/**

* 入队

*

* @param e

*/

void enqueue(E e);

/**

* 出队

*

* @return

*/

E dequeue();

/**

* 获取队首元素

*

* @return

*/

E getFront();

/**

* 获取队列中元素的个数

*

* @return

*/

int getSize();

/**

* 判断队列是否为空

*

* @return

*/

boolean isEmpty();

}

接口实现:LoopQueue

public class LoopQueue implements Queue {

/**

* 承载队列元素的数组

*/

private E[] data;

/**

* 队首的位置

*/

private int front;

/**

* 队尾的位置

*/

private int tail;

/**

* 队列中元素的个数

*/

private int size;

/**

* 指定容量,初始化队列大小

* (由于循环队列需要浪费一个空间,所以我们初始化队列的时候,要将用户传入的容量加1)

*

* @param capacity

*/

public LoopQueue(int capacity) {

data = (E[]) new Object[capacity + 1];

}

/**

* 模式容量,初始化队列大小

*/

public LoopQueue() {

this(10);

}

@Override

public void enqueue(E e) {

// 检查队列为满

if ((tail + 1) % data.length == front) {

// 队列扩容

resize(getCapacity() * 2);

}

data[tail] = e;

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

size++;

}

@Override

public E dequeue() {

if (isEmpty()) {

throw new IllegalArgumentException("队列为空");

}

// 出队元素

E element = data[front];

// 元素出队后,将空间置为null

data[front] = null;

// 维护front的索引位置(循环队列)

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

// 维护size大小

size--;

// 元素出队后,可以指定条件,进行缩容

if (size == getCapacity() / 2 && getCapacity() / 2 != 0) {

resize(getCapacity() / 2);

}

return element;

}

@Override

public E getFront() {

if (isEmpty()) {

throw new IllegalArgumentException("队列为空");

}

return data[front];

}

@Override

public int getSize() {

return size;

}

@Override

public boolean isEmpty() {

return front == tail;

}

// 队列快满时,队列扩容;元素出队操作,指定条件可以进行缩容

private void resize(int newCapacity) {

// 这里的加1还是因为循环队列我们在实际使用的过程中要浪费一个空间

E[] newData = (E[]) new Object[newCapacity + 1];

for (int i = 0; i < size; i++) {

// 注意这里的写法:因为在数组中,front 可能不是在索引为0的位置,相对于i有一个偏移量

newData[i] = data[(i + front) % data.length];

}

// 将新的数组引用赋予原数组的指向

data = newData;

// 充值front的位置(front总是指向队列中第一个元素)

front = 0;

// size 的大小不变,因为在这过程中,没有元素入队和出队

tail = size;

}

private int getCapacity() {

// 注意:在初始化队列的时候,我们有意识的为队列加了一个空间,那么它的实际容量自然要减1

return data.length - 1;

}

@Override

public String toString() {

return "LoopQueue{" +

"【队首】data=" + Arrays.toString(data) + "【队尾】" +

", front=" + front +

", tail=" + tail +

", size=" + size +

", capacity=" + getCapacity() +

'}';

}

}

测试类:LoopQueueTest

public class LoopQueueTest {

@Test

public void testLoopQueue() {

LoopQueue loopQueue = new LoopQueue<>();

for (int i = 0; i < 10; i++) {

loopQueue.enqueue(i);

}

// 初始化队列数据

System.out.println("原始队列: " + loopQueue);

// 元素0出队

loopQueue.dequeue();

System.out.println("元素0出队: " + loopQueue);

loopQueue.dequeue();

System.out.println("元素1出队: " + loopQueue);

loopQueue.dequeue();

System.out.println("元素2出队: " + loopQueue);

loopQueue.dequeue();

System.out.println("元素3出队: " + loopQueue);

loopQueue.dequeue();

System.out.println("元素4出队,发生缩容: " + loopQueue);

// 队首元素

System.out.println("队首元素:" + loopQueue.getFront());

}

}

测试结果:

原始队列: LoopQueue{【队首】data=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, null]【队尾】, front=0, tail=10, size=10, capacity=10}

元素0出队: LoopQueue{【队首】data=[null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null]【队尾】, front=1, tail=10, size=9, capacity=10}

元素1出队: LoopQueue{【队首】data=[null, null, 2, 3, 4, 5, 6, 7, 8, 9, null]【队尾】, front=2, tail=10, size=8, capacity=10}

元素2出队: LoopQueue{【队首】data=[null, null, null, 3, 4, 5, 6, 7, 8, 9, null]【队尾】, front=3, tail=10, size=7, capacity=10}

元素3出队: LoopQueue{【队首】data=[null, null, null, null, 4, 5, 6, 7, 8, 9, null]【队尾】, front=4, tail=10, size=6, capacity=10}

元素4出队,发生缩容: LoopQueue{【队首】data=[5, 6, 7, 8, 9, null]【队尾】, front=0, tail=5, size=5, capacity=5}

队首元素:5

完整版代码GitHub仓库地址:Java版数据结构-队列(循环队列) 欢迎大家【关注】和【Star】

至此笔者已经为大家带来了数据结构:静态数组、动态数组、栈、数组队列、循环队列;接下来,笔者还会一一的实现其它常见的数组结构,大家一起加油。

静态数组

动态数组

数组队列

循环队列

链表

循环链表

二分搜索树

优先队列

线段树

字典树

AVL

红黑树

哈希表

....

持续更新中,欢迎大家关注公众号:小白程序之路(whiteontheroad),第一时间获取最新信息!!!

循环队列的创建Java_Java版-数据结构-队列(循环队列)相关推荐

  1. 循环队列的java结构_java数据结构之循环队列(数组实现)

    package com.ws.队列.数组环形队列; //环形数组队列 //判断满:尾+1%队列长度==头 //添加数据:要(尾+1)%数组长度 //取出数据:要(头+1)%数组长度 因为这两个都是循环 ...

  2. java 数据结构_Java版-数据结构-队列(数组队列)

    前言 看过笔者前两篇介绍的 Java版数据结构 数组和 栈的盆友,都给予了笔者一致的好评,在这里笔者感谢大家的认可!!! 由于本章介绍的数据结构是 队列,在队列的实现上会基于前面写的 动态数组来实现, ...

  3. 数据结构Python版(四)——队列

    目录 一.队列简介 二.顺序队列 2.1 非循环队列 2.2 循环队列 2.2.1 假溢出 2.2.2 循环队列的架构 2.2.3 循环队列的实现 三.链队 3.1 链队的完整实现 四.双端队列 4. ...

  4. JS版数据结构第三篇(链表)

    链表分为单链表,双链表,以及环形链表,我将分三个模块分别介绍,并有相应的题目对应讲解. 单链表 定义 还是按照老规矩先看一下百度百科对单链表的定义 根据以上文字我们可以得出 单链表是一种链式的数据结构 ...

  5. java循环队列_Java版-数据结构-队列(循环队列)

    前情回顾 在上一篇,笔者给大家介绍了数组队列,并且在文末提出了数组队列实现上的劣势,以及带来的性能问题(因为数组队列,在出队的时候,我们往往要将数组中的元素往前挪动一个位置,这个动作的时间复杂度O(n ...

  6. 数据结构(七)---循环队列的实现---java版

    ---------------------------------------------接口定义---------------------------------- package com.cn.h ...

  7. 数据结构——链式队列解析(C语言版)

    摘自:数据结构学习--链式队列解析(C语言版) 作者:正弦定理 发布时间:2020-11-26 21:07:08 网址:https://blog.csdn.net/chinesekobe/articl ...

  8. 常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构)

    常见数据结构和算法实现(排序/查找/数组/链表/栈/队列/树/递归/海量数据处理/图/位图/Java版数据结构) 数据结构和算法作为程序员的基本功,一定得稳扎稳打的学习,我们常见的框架底层就是各类数据 ...

  9. 数据结构与算法(Python版) | (6) 线性结构---队列、双端队列和列表

    本专栏主要基于北大的数据结构与算法教程(Python版)进行整理,包括课程笔记和OJ作业. 课程链接 1. 队列抽象数据类型及Python实现 什么是队列? 队列是一种有次序的数据集合,其特征是: 1 ...

  10. mysql循环队列_数据结构:循环队列

    数据结构:循环队列 写在前面 数组表示的问题 对于队列最好的方法是使用链表实现,因为对于数组来说,队列可能会出现下面这种情况: 如图所示,不可以继续添加元素,否则会造成数组越界而遭致程序出错.然而此时 ...

最新文章

  1. 机器学习笔记: Upsampling, U-Net, Pyramid Scene Parsing Net
  2. Boost:can_queryr的使用测试程序
  3. java 缓冲流_Java缓冲流的使用
  4. SpaceVim 1.1.0 发布,模块化 Vim IDE
  5. 树莓派的ssh远程登录操作(图文)
  6. python创建透明窗体_python – PyQt5:使用不透明的子项创建半透明窗口
  7. linux如何确认账号过期了,linux下非root用户秘密过期如何确认,如果确认,该如何延期使其有效?...
  8. 配置Firefox火狐浏览器burpsuite https抓包
  9. 156种PS特效动作中文大合集【附高清视频教程】
  10. 目标客户画像_做营销时,如何做好目标用户群体画像?
  11. 查看CPU最大支持内存容量
  12. excel转vcf 易语言免费版
  13. English digest
  14. c语言的中打印出鸟图形,教你画出一只萌萌哒小鸟【PS教程】
  15. 使用druid-spring-boot-starter时设置监控界面登录信息的方法
  16. C语言程序课程设计—读心术
  17. 内测“今视频”APP入局长视频,快手有钱之后“飘了”?
  18. 36_2 On Chip Bus —— AXI总线介绍
  19. 2022-2028全球数据中心开放式机架行业调研及趋势分析报告
  20. OpenCV找圆系列(2)HoughCircles算子新增了HOUGH_GRADIENT_ALT方法,效果好多了

热门文章

  1. Java基础:查漏补缺
  2. Javascript特效:轮播图
  3. Javascript特效:循环抽奖
  4. 沈是计算机专业考研分数线,计算机#2017年东北大学计算机考研究生分数线_计算机考研究生复试分数线...
  5. LIO-SAM探秘第三章之代码解析(五) --- imuPreintegration.cpp
  6. OPENCV中操作鼠标
  7. PP-YoLoE | PP-YoLov2全面升级Anchor-Free,速度精度完美超越YoLoX和YoLov5
  8. [error]:启用sqlserver配置管理器异常,内存不足
  9. 面向集团客户云计算运营平台的市场情况及产品发展——之云计算运营平台方案(二)...
  10. 2019.7.26随堂笔记