队列(FIFO)也就是一种先进先出的数据结构,它有很多种实现方式,链表、数组,下面讲的就是如果利用循环数组来实现队列。
如果利用单纯的利用数组 它的出队 (后面的数组都要向前移动)时间复杂度是O(n),但是如果把数组实现成一个循环的数组
出队时 front加一就好了 后面的数组不必一一向前移动,那么他的时间复杂度就可以变为O(1),下面就直接撸代码
public class LoopQueue<E> {
private E[] data;
private int front;//表示队首头部的索引
private int tail;//表示队尾的索引
private int size;//表示数据的大小
public LoopQueue(int capacity){
//数组的容量 多出一个 不存任何数组 因为用它来区分 空 和 数组满的两种情况 当tail == front表示数组为空
(tail+1)%data.legth == front 表示数组满了
data = (E[]) new Object[capacity+1];
}
public LoopQueue(){
this(10);
}
public int getCapacity(){
//真正的数据容量也就是数组长度减一 因为预留出一个位置什么也不存
return data.length-1;
}
public boolean isEmpty(){
//表示数组为空
return front == tail;
}
public boolean isFull(){
//这里就提现出那个预留一个位置不存任何数组位置的作用了 这里就表示数组满了
return (tail+1)%data.length == front;
}
public void enqueue(E e){
//数组满了 需要扩容了 这次每次扩容 为原数组容量的2倍
if((tail+1)%data.length == front){
resize(getCapacity()*2);
}
//在数组尾部添加一个元素
data[tail] = e;
//tail加一 因为这是一个循环数组 所以要进行取模
tail = (tail+1)%data.length;
//数组长度加一
size++;
}
public void resize(int newCapacity){
//这里同样多加了一个数组空间 用了什么也不存
E[] newData = (E[]) new Object[newCapacity+1];
for(int i=0;i<size;i++){
//data 数组是从 front开始的 记得取模 因为他是一个循环 然后复制给新的数组
newData[i] = data[(front+i)%data.length];
}
//那么现在的tail位置就是数组长度位置
tail = size;
//front 从0开始 因为把它重新复制给了一个新的数组
front = 0;
data = newData;
}
public E dequeue(){
//取出队首数组
E e = data[front];
//取出之后 font就要进行+1 因为他是一个循环所以要进行加一取模
front = (front+1)%data.length;
//数组大小减-
size--;
// 如果数组长度为容量的1/4时候 且容量长度除2不为0 那么就重新缩容。 这里为什么在数组长度为容量1/4的时候进行缩容呢而不 是1/2呢,这里是有原因的 如果当数组为1/2的时候缩容,然后添加一个数据 然后有扩容,又删除一个数据缩容,复杂度就会进行震荡而1/4的时候 缩容为数组的1/2很好的错开了这个加一扩容 减一缩容的时间复杂的震荡
if(size == getCapacity()/4 && getCapacity()/2!=0){
resize(getCapacity()/2);
}
return e;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append(String.format("capacity %d size %d front\n[", getCapacity(), size));
for(int i=front;i != tail;i = (i+1)%data.length){
builder.append(data[i]);
if((i+1)%data.length != tail){
builder.append(",");
}
}
builder.append("]");
return builder.toString();
}
}