循环队列
引入
为什么要使用循环队列?因为普通队列的底层是数组,对于队列的先进先出,每当我们要删除队列首一个元素的时候,之后的元素都要向前推移,然后size--
,这样的话就增加算法的时间复杂度(变成了O(n))。这时我们就想到,队首元素弹出去了,即使不移动后续元素,他仍会保持队列的样子,我们只需要用一个变量来存储队首索引front
,再用一个变量来存储队尾的后一个索引tail
。
逻辑
当front == tail
时 队列为空。
插入元素后tail ++
元素出队后front ++
这时,我们来考虑一下极端的条件,当最后一个元素插入到data[7]
的位置后,tail就不可以++了,这时候把队列想象成一个环形的,查看前方是否有弹出队列后为空的位置,也就是(当前索引 + 1) / capacity = 0
。
那么队列为满又如何判断呢?
通过下图我们看到的是tail + 1 = front
,同时我们也看到了我们浪费了一个数组空间,所以是准确的来说队列为满的条件是(tail + 1) % capacity == front
。所以在我们后面写代码的时候要对于用户创建的数组空间要多开辟一个 。
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity){
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 int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
代码
在写代码的时候,代码也会有逻辑的,当我们再向循环队列插入元素的时候要判断队列是否是满的,((tail + 1) % data.length == front
)。
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity){
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 int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front){
resize(getCapacity() * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
private void resize(int newCapacity){
E[] newData = (E[]) new Object[newCapacity + 1];
for (int i = 0; i < size; i++){
newData[i] = data[(i + front) % data.length];
}
data = newData;
front = 0;
tail = 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;
}
@Override
public E getFront() {
if (isEmpty())
throw new IllegalArgumentException("Queue is empty.");
return data[front];
}
@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();
}
public static void main(String[] args) {
LoopQueue<Integer> queue = new LoopQueue<>();
for (int i = 0; i < 10;i++){
queue.enqueue(i);
System.out.println(queue);
if (i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
源码
https://github.com/909913825/Queue/tree/master/src