java循环队列_Java实现数据结构之【循环队列】

上篇文章介绍了队列这个数据结构,发现普通数组队列,它的出队操作时间复杂度是O(n)的,因为数组第一个元素被移除后,后面的所有元素都要往前挪,导致每次执行出队操作的都会进行多次元素前移的操作(如下图),为了优化出队操作的性能消耗,我们可以使用循环队列

为什么循环队列能够解决这种性能消耗的问题呢?我们来看一下循环队列的运行过程吧

入队操作

出队操作

根据上面三张图相比两种队列,普通队列第一个元素被移除后,后面的所有元素都往前挪,消耗性能较多,而循环队列维护了两个变量 Front 和 Tail,Front指向了循环队列的队首,每次移除队首元素后,Front就会指向后面一个元素,所以每次移除队首元素后,Front就会往后挪一个单位,当循环队列中所有元素都被移除后,Front和Tail会指向同一个位置,也就是说当Front=Tail时,循环队列为空,下面我们看下继续往循环队列中添加元素时,会是什么样的情况。

首先可以看出,继续往循环队列中添加元素时,Front依旧指向了队首,而Tail在最后一个位置添加元素后,重新指向了前面没有元素的位置,当Tail指向下标为2的位置时,不再添加元素,因为如果此时再次往循环队列中添加元素,会再次出现Front=Tail的情况,但是我们都知道Front=Tail是表示循环队列为空,为了不出现这种情况,刻意留一个空位,可以定义当Tail在Front前一个位置的时候,也就是Tail+1=Front代表循环队列已满,但是出现下图中的情况时,Tail+1并不是等于Front,所以Tail+1是不合理的,应该将Tail+1对循环队列的长度取余,拿下面的图举例,(Tail+1)%4=Front,Tail=3,Front=0,(Tail+1)%4也就等于0,所以(Tail+1)%4=Front代表循环队列已满是合理的。

下面看下代码实现

public class LoopQueue implements Queue {

private E[] data;

private int front;

private int tail;

private int size;

@SuppressWarnings("unchecked")

public LoopQueue(int capacity) {

/*

* 这里的capacity加1的原因是,在循环队列中, 我们会浪费一个空间,这样能存元素的数量会少一个,

* 所以这里我们基于传进来的容量+1,这样就可以储存期望的元素数量了

*/

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

front = 0;

tail = 0;

size = 0;

}

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("Cannot dequeue from an empty queue.");

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;

}

@SuppressWarnings("unchecked")

private void resize(int newCapacity) {

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

for(int i=0;i

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

}

data=newData;

front=0;

tail=size;

}

public int getCapacity() {

// 减一是因为我们初始化的时候进行了+1操作

return data.length - 1;

}

@Override

public E getFront() {

if(isEmpty())

throw new IllegalArgumentException("Queue is emtpy.");

return data[front];

}

@Override

public int getSize() {

return size;

}

@Override

public boolean isEmpty() {

return front == tail;

}

@Override

public String toString() {

StringBuilder res = new StringBuilder();

res.append(String.format("Queue: size = %d , capacity = %d\n", size, getCapacity()));

res.append("[");

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("]");

return res.toString();

}

}

通过对维护Front和Tail参数,就可以将队列的出队操作变成O(1)了。

我们用下面的测试代码比较一下ArrayQueue和LoopQueue的性能差距

输出结果

可以看出在10万次入队和出队的操作下LoopQueue比ArrayQueue的效率高出百倍,所以在通过循环队列优化性能是有意义的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值