java循环队列_Java循环队列原理与用法详解

本文详细讲解了Java循环队列的工作原理和实现,包括如何解决顺序队列出队时的时间复杂度问题,以及如何通过循环队列实现O(1)的出队操作。此外,还介绍了循环队列的扩容和缩容策略,以及提供的测试用例来验证其正确性。
摘要由CSDN通过智能技术生成

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

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

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

dedfd8fdb8660d069251fbf5f27b9c49.png

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

#出队

df4e033c18f1c621b7dea7d89cda4c76.png

#2整体前移一位

ef6ea46866ba40b12e7c257e49c14832.png

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

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

262a5f6bf8729d0efc6a0266f6897d3f.png

2.循环队列原理

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

9de9f1d85fb5833809cece36aaa192ea.png

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

5832b65ff0f5313ad59c036f64d8764d.png

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

10929ea93f798f6ca34584cff80146a7.png

(4)入队元素,tail叠加

91f6f7ee36ac0a3437c70bf7f90f1216.png

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

4a45ef46c272b21326e780b8da179b1e.png

此时数组应该变为这样:

d0f5dc5879cf7b2eac1a3cf6f11a66be.png

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

964ee006a3e4b96fd95679a2c7a67bf1.png

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

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

3.循环队列代码实现

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

#1:接口Queue代码如下:

package Queue;

public interface Queue {

//获取队列中元素个数

int getSize();

//队列中元素是否为空

boolean isEmpty();

//入队列

void enqueue(E e);

//出队列

E dequeue();

//获取队首元素

E getFront();

}

#2:LoopQueue相关代码:

package Queue;

//循环队列

public class LoopQueue implements Queue {

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 queue = new LoopQueue();

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);

}

}

}

结果为:

fda25d4c3327a90cf8e056c44965b01c.png

4.循环队列时间复杂度

b01aef472ac4b9861683555ce9867e6f.png

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值