Java–循环队列–基于泛型数组
一、为什么需要循环队列?
循环队列使出队操作的时间复杂度降到了O(1)级别
二、底层维护:
-
data : 泛型数组
-
front: 指向队首
-
tail : 指向队尾(下一个待插入的位置
-
size : 实际元素个数
三、基于数组的循环队列以一个额外空间为代价维护以下条件:
- 队列空:front = tail
- 队列满:(tail+1)%data.length = front
(tail+1) % data.length
:是什么意思?以循环的方式向后移动一位
四、原理图:
—
五、注意
- 涉及队尾,队首指针的移动必须以循环的方式移动,而不是直接"++"就完事
-
(tail+1)%data.length
-
(front+1)%data.length
- 基于数组的循环队列的遍历方式
/**
*
* @author a_apple
* @create 2020-04-14 21:00
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity) {
//用户期望存储capacity个元素,但底层需要浪费一个空间维护,所以创建capacity+1
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
//获取队列 实际可存储最大容量
public int getCapacity() {
return data.length - 1;
}
//队尾进队
@Override
public void enQueue(E e) {
//1.先判断队列是否满了,满了要扩容
if (isFull()) {
resize(getCapacity() * 2);
}
data[tail] = e;
//tail以循环的方式向后移一位
tail = (tail + 1) % data.length;
size++;
}
//队首出队 时间复杂度变成了O(1)
@Override
public E deQueue() {
//先判断是否队列空
if (isEmpty()) {
throw new IllegalArgumentException("deQueue error, Queue is Empty!");
}
E ret = data[front];
//front以循环的方式向后移一位
front = (front + 1) % data.length;
size--;
//当size == capacity/4 ,进行缩容操作,并且缩容的容量!=0
if (size == getCapacity() / 4 && getCapacity() / 2 != 0)
resize(getCapacity() / 2);
return ret;
}
//扩容
private void resize(int newCapacity) {
//扩容时,同样需要多添加一个额外的空间
E[] newData = (E[]) new Object[newCapacity + 1];
//遍历循环队列的一种方式
for (int i = 0; i < size; i++) {
//把front到tail复制到newData
//把front 放到i=0 front+1 放到i=1 front+2 放到i=2
//为啥还要 % data.length ?因为front+i可能会>size-1,此时需要返回到队首
newData[i] = data[(front + i) % data.length];
}
data = newData;
front = 0;
tail = size; //下一个待进队的位置
}
//获取队首元素
@Override
public E getFront() {
//先判断是否队列空
if (isEmpty()) {
throw new IllegalArgumentException("getFront error, Queue is Empty!");
}
return data[front];
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
//判断队列满:(tail + 1) % data.length == front
public boolean isFull() {
return (tail + 1) % data.length == front;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Queue: size=%d,capacity=%d\n", size, getCapacity()));
sb.append(" front [");
//i循环的向后移1位,因为i可能会大于size-1
for (int i = front; i != tail; i = (i + 1) % data.length) {
sb.append(data[i]);
//判断是否是最后一个元素,因为最后一个元素的下一个元素坐标就是tail
if ((i + 1) % data.length != tail) {
sb.append(",");
}
}
sb.append("] tail");
return sb.toString();
}
}