Java循环队列原理与用法详解

作者:WFaceBoss 时间:2023-11-13 20:05:36 

本文实例讲述了Java循环队列原理与用法。分享给大家供大家参考,具体如下:

在正式进行循环队列学习之前,我们先来看看在顺序队列中删除队首元素出现的问题

(1)设一个容量为capacity=8,size=5(a,b,c,d,e)的数组,左侧为队首、右侧为队尾。

Java循环队列原理与用法详解

(2)出队一个元素后,需整体往前移动一位

#出队

Java循环队列原理与用法详解  

#2整体前移一位

Java循环队列原理与用法详解

关于该种操作方式我们很容易得出时间复杂度为O(n)。

这时我们就想可不可以在出队元素后,整体元素不往前移,而是在数组中记下队首front是谁,同时队尾tail指向在下一次元素入队时的位置,这样当再有出队时只需要维护一下front的指向即可,而不需移动元素。就这样我们就有了循环队列的情况。

Java循环队列原理与用法详解

2.循环队列原理

(1)初始,数组整体为空时,队首front、队尾tail指向同一个位置(数组索引为0的地方)也即front==tail 时队列为空

Java循环队列原理与用法详解

 

(2)当往数组中添加元素后,

Java循环队列原理与用法详解

(3)出队一个元素,front指向新的位置

Java循环队列原理与用法详解

(4)入队元素,tail叠加

Java循环队列原理与用法详解

(5)当tail不能再增加时,数组前面还有空余,此时循环队列就该出场了。

 Java循环队列原理与用法详解

此时数组应该变为这样:

Java循环队列原理与用法详解

 在往数组中添加一个元素:

Java循环队列原理与用法详解

这样数组就已经满了(tail+1==front 队列满),开始出发扩容操作。【capacity中,浪费一个空间】。

为了tail能返回到数组的前面位置,将队列满的表达式变为 【(tail+1)%c==front】这样数组就可以循环移动了。

3.循环队列代码实现

新建一个类LoopQueue并实现接口Queue。

#1:接口Queue代码如下:


package Queue;

public interface Queue<E> {
 //获取队列中元素个数
 int getSize();

//队列中元素是否为空
 boolean isEmpty();

//入队列
 void enqueue(E e);

//出队列
 E dequeue();

//获取队首元素
 E getFront();
}

#2:LoopQueue相关代码:


package Queue;

//循环队列
public class LoopQueue<E> implements Queue<E> {
 private E[] data;
 private int front, tail;
 private int size;//队列中元素个数

//构造函数,传入队列的容量capacity构造函数
 public LoopQueue(int capacity) {
   data = (E[]) new Object[capacity + 1];//浪费与一个空间
   front = 0;
   tail = 0;
   size = 0;
 }

//无参构造函数,默认队列的容量capacity=10
 public LoopQueue() {
   this(10);
 }

//真正容量
 public int getCapacity() {
   return data.length - 1;
 }

//队列是否为空
 @Override
 public boolean isEmpty() {
   return front == tail;
 }

//队列中元素个数
 @Override
 public int getSize() {
   return size;
 }

//入队列操作
 @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 ret = data[front];
   data[front] = null;//手动释放
   front = (front + 1) % data.length;
   size--;
   if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
     resize(getCapacity() / 2);
   }
   return ret;
 }

//获取队首元素
 @Override
 public E getFront() {
   if (isEmpty()) {
     throw new IllegalArgumentException("队列为空");
   }
   return data[front];
 }

//改变容量
 private void resize(int newCapacity) {
   E[] newData = (E[]) new Object[newCapacity + 1];
   for (int i = 0; i < size; i++) {
     newData[i] = data[(front + i) % data.length];//循环数组防止越界
   }
   data = newData;
   front = 0;
   tail = size;
 }

@Override
 public String toString() {
   StringBuilder res = new StringBuilder();
   res.append(String.format("Queue:size=%d, capacity=%d\n", size, getCapacity()));
   res.append("front [");
   for (int i = front; i != tail; i = (i + 1) % data.length) {
     res.append(data[i]);
     if ((i + 1) % data.length != tail) {
       res.append(",");
     }
   }
   res.append("] tail");
   return res.toString();
 }

}

在关于LoopQueue类中需要注意的:

(1)第11行中的+1是capacity需要浪费一个空间,故在实例化是多加1


data = (E[]) new Object[capacity + 1];//浪费与一个空间

(2)地24行真正的容量是data.length-1,这是由于有一个空间是浪费的。


data.length - 1;

(3)关于入队中第46行tail值的说明

为了保证入队是循环操作,tail值的变化规律为


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

(4)关于82行的数据迁移操作,取余操作是为了防止循环数组时越界。


 newData[i] = data[(front + i) % data.length];//循环数组防止越界

#3直接在LoopQueue中添加一个main函数进行测试,相关代码如下:


 //测试用例
 public static void main(String[] args) {
   LoopQueue<Integer> queue = new LoopQueue<Integer>();
   for (int i = 0; i < 10; i++) {
     queue.enqueue(i);
     System.out.println(queue);

if(i%3==2){//每添加3个元素出队列一个
       queue.dequeue();
       System.out.println(queue);
     }

}

}

结果为:

 Java循环队列原理与用法详解

4.循环队列时间复杂度

Java循环队列原理与用法详解

到此我们就实现了一个循环队列操作,解决了在顺序队列中出队时的时间复杂度为O(n)的情况,在循环队列中出队的时间复杂度为O(1)。

源码地址 https://github.com/FelixBin/dataStructure/blob/master/src/Queue/LoopQueue.java

希望本文所述对大家java程序设计有所帮助。

来源:https://www.cnblogs.com/wfaceboss/p/10628345.html

标签:Java,循环队列
0
投稿

猜你喜欢

  • 消息队列-kafka消费异常问题

    2023-02-02 07:08:05
  • Spring中Bean扫描原理详情

    2022-05-26 04:33:14
  • TOMCAT内存溢出及大小调整的实现方法

    2023-02-24 06:55:33
  • Java Struts图片上传至指定文件夹并显示图片功能

    2023-03-15 10:48:51
  • SpringBoot拦截器的配置使用介绍

    2021-06-20 07:25:54
  • C#环形缓冲区(队列)完全实现

    2022-06-26 08:05:48
  • C#使用第三方组件生成二维码汇总

    2023-10-03 22:15:21
  • 解决Spring在Thread中注入Bean无效的问题

    2022-06-26 13:03:59
  • android 大图片拖拽并缩放实现原理

    2022-11-10 05:59:55
  • Java实现拖拽列表项的排序功能

    2023-11-28 23:39:00
  • C#实现简单的RSA非对称加密算法示例

    2022-07-09 18:16:37
  • C#虚函数用法实例分析

    2022-04-03 15:03:42
  • Spring boot2+jpa+thymeleaf实现增删改查

    2021-06-02 07:21:49
  • Java语法关于泛型与类型擦除的分析

    2023-12-22 06:15:22
  • C# WPF 自定义按钮的方法

    2021-08-30 23:42:11
  • c#批量抓取免费代理并且验证有效性的实战教程

    2023-12-19 23:33:30
  • c# BackgroundWorker组件的作用

    2022-12-23 20:56:12
  • Java虚拟机使用jvisualvm工具远程监控tomcat内存

    2023-11-28 22:15:49
  • 使用Java实现三种等级的扫雷游戏(完整版)

    2023-05-10 07:34:17
  • Android 安全加密:非对称加密详解

    2021-08-03 18:39:43
  • asp之家 软件编程 m.aspxhome.com