数组队列中的出队,在时间复杂度上是O(n)复杂度,因为你删除数组首端,后面的数据都要左移一位。
我们可以记录一下队首的索引和队尾索引,入队时队尾索引+1,出队时队首索引-1,当队首索引与队尾索引相等时代表队列空了,完成一次循环。
入队五个元素,队尾索引(tail)自增五次
出队一个元素则队首索引(front)自增一次。
当数组尾部填满的时候不着急扩充,先看看数组首部是不是还有空余,有空余则指向数组首部。
此时才不能继续入队,若想要继续入队只能进行扩容,毕竟tail == front则队列会被视为空,我们有意识的浪费掉一个空间。
队尾索引+1对数组长度进行取余等于队首索引则队列满
(tail + 1) % capacity. length == front
实现代码:
/**
* 循环队列,不使用002章编写的动态数组,我们需要从底层写起
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private Integer tail, front;
/**
* 创建队列时指定队列最大容量
*
* @param capacity
*/
public LoopQueue(Integer capacity) {
// +1是因为循环数组会浪费一个空间,不明白可以看007号文档
data = (E[]) new Object[capacity + 1];
tail = 0;
front = 0;
}
/**
* 默认最大容量为10
*/
public LoopQueue() {
this(10);
}
@Override
public void enqueue(E e) {
//队列满了就去扩容为当前最大容量的两倍
if ((tail + 1) % data.length == front)
this.reCapacity((this.getCapacity() * 2));
data[tail] = e;
tail = (tail + 1) % data.length;//保证不大于等于数组总容量
}
@Override
public E dequeue() {
E buffer = null;
if (tail != front) {
buffer = data[front];
data[front] = null;//删除掉且队首索引自增
front = (front + 1) % data.length;//保证大于等于数组总容量
//队列只占用总容量的四分之,则缩小为当前最大容量的一半
if (this.getSize() <= this.getCapacity() / 4 && this.getCapacity() / 2 != 0)
this.reCapacity(this.getCapacity() / 2);
}
return buffer;
}
@Override
public E getFront() {
return data[front];
}
@Override
public Integer getSize() {
if (tail > front)
//当队尾索引大于队首索引时:队列size = tail - front
return tail - front;
else if (tail < front)
//当队尾索引不为0且小于队首索引时:队列size = data.length - tail
return data.length - tail;
else if (tail == 0 && front != 0)
//当队尾索引等于0且队首不为0时:队列size = data.length - front
return data.length - front;
return 0;//当队尾索引等于队首索引时:队列size = =0
}
@Override
public Boolean isEmpty() {
return this.getSize() == 0;
}
/**
* 获取队列的最大容量
*
* @return
*/
public Integer getCapacity() {
return data.length - 1;//因为创建的时候+!了,所以获取的时候要-1
}
/**
* 重新设置队列的最大容量
*
* @param newCapacity
*/
private void reCapacity(Integer newCapacity) {
E[] newData = (E[]) new Object[newCapacity + 1];
Integer size = this.getSize();
for (int i = 0; i < size; i++)
// 因为是循环数组有一定的偏移,队首索引可能会大于队尾索引,所以要对原数组容量进行取余
newData[i] = data[(i + front) % data.length];
data = newData;
front = 0;
tail = size;
}
public String toString() {
String buffer = "";
Integer size = this.getSize();
for (int i = 0; i < size; i++) {
// 因为是循环数组有一定的偏移,队首索引可能会大于队尾索引,所以要对原数组容量进行取余
buffer += data[(i + front) % data.length].toString();
if (i != size - 1)
buffer += ", ";
}
// 最左侧是队列首部
return "Size:" + size + "\tLoopQueue[" + buffer + "]";
}
}