(一)队列
队列也是一种线性结构,相比数组,队列对应的操作是数组的子集。队列只能从队尾添加元素,从队首取出元素。并且,队列是一种先进先出的数据结构First In First Out (FIFO)
(二)队列的实现
队列接口实现如下:
public interface Queue<E>{
void enqueue(E e);//入列
E dequeue();//出列
E getFront();//获取队列的第一个元素
int getSize();//获取队列的大小
boolean isEmpty();//判断队列是否为空
}
基于动态数组的队列:
- 声明
public class ArrayQueue<E> implements Queue<E> { private Array<E> array; public ArrayQueue(int capacity){ array = new Array<>(capacity); } public ArrayQueue(){ array = new Array<>(); } public int getCapacity(){ return array.getCapacity(); } //队列的输出格式 @Override public String toString(){ StringBuilder res = new StringBuilder(); res.append("Queue: "); res.append("front ["); for (int i = 0; i < array.getSize(); i++){ res.append(array.get(i)); if(i != array.getSize() - 1) res.append(", "); } res.append("] tail"); return res.toString(); } }
- 出列
@Override public E dequeue(){ return array.removeFirst(); }
- 入列
@Override public void enqueue(E e){ array.addLast(e); }
- 获取队列的第一个元素
@Override public E getFront(){ return array.getFirst(); }
- 获取队列的大小
@Override public int getSize(){ return array.getSize(); }
- 判断队列是否为空
@Override public boolean isEmpty(){ return array.isEmpty(); }
(三)数组队列的复杂度分析
- ArrayQueue<E>
- void enqueue(E e); O(1)均摊
- E dequeue(); O(n)
- E getFront(); O(1)
- int getSize(); O(1)
- boolean isEmpty(); O(1)
(四)循环队列
- front == tail 队列为空
- (tail + 1) % capacity == front 队列满
- capacity中浪费一个空间
(五)循环队列的实现
- 声明
public class LoopQueue<E> implements Queue<E> { private E[] data; private int front,tail;//队首、队尾索引 private int size;//队列的大小,只使用front,tail怎么计算size? public LoopQueue(int capacity){ data = (E[])new Object[capacity + 1];//循环队列要浪费一个空间,故+1 front = 0; tail = 0; size = 0; } public LoopQueue() { this(10); } public int getCapacity(){ return data.length - 1; } //循环队列的输出方式 @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){//循环队列遍历方式2 res.append(data[i]); if ((i + 1) % data.length != tail) res.append(", "); } res.append("] tail"); return res.toString(); } }
- 判断循环队列是否为空
@Override public boolean isEmpty(){ return front == tail; }
- 获取循环队列的大小
@Override public int getSize(){ return size; }
- 向循环队列中添加元素e
@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; }
- 获取循环队列的第一个元素
@Override public E getFront(){ if (isEmpty())//判断队列是否为空 throw new IllegalArgumentException("cannot dequeue from an empty queue"); return data[front]; }
- 扩容或缩容
private void resize(int capacity){ E[] newData = (E[])new Object[capacity + 1]; for (int i = 0; i < size; i ++)//循环队列遍历方式1 newData[i] = data[(i + front) % data.length]; data = newData; front = 0; tail = size; }
(六)循环队列的复杂度分析
- LoopQueue<E>
- void enqueue(E e); O(1)均摊
- E dequeue(); O(1)均摊
- E getFront(); O(1)
- int getSize(); O(1)
- boolean isEmpty(); O(1)
(七) 数组队列和循环队列的比较
import java.util.Random;
public class Main {
//测试使用q运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
private static double testQueue(Queue<Integer> q, int opCount){
long startTime = System.nanoTime();//记录时间,以纳秒为单位
Random random = new Random();
for (int i = 0; i < opCount; i++)
q.enqueue(random.nextInt(Integer.MAX_VALUE));
for (int i = 0; i < opCount; i++)
q.dequeue();
long endTime = System.nanoTime();
return (endTime - startTime ) / 1000000000.0;
}
public static void main(String[] args) {
// write your code here
int opCount = 100000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
double time1 = testQueue(arrayQueue,opCount);
System.out.println("ArrayQueue, time: " + time1 + "s");
LoopQueue<Integer> loopQueue = new LoopQueue<>();
double time2 = testQueue(loopQueue, opCount);
System.out.println("LoopQueue, time: " + time2 + "s");
}
}