序
线性结构
相比数组,队列相应的操作是数组的子集
队列的出队、入队方式:先进先出(FIFO)
实现内容
1、数组队列
2、循环队列
3、比较数组队列和循环队列的性能差异
基本功能:
// Queue 接口
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e); // 出队
E dequeue(); // 入队
E getFront(); // 查看队首元素
}
具体代码
1、数组队列:基于之前的笔记中自己封装的数组Array实现一个队列。
public class ArrayQueue<E> implements Queue<E>{
private Array<E> array;
public ArrayQueue(int capacity){
array = new Array<>(capacity);
}
public ArrayQueue(){
array = new Array<>();
}
@Override
public int getSize(){
return array.getSize();
}
@Override
public boolean isEmpty(){
return array.isEmpty();
}
public int getCapacity(){
return array.getCapacity();
}
@Override
public void enqueue(E e){
array.addLast(e);
}
@Override
public E dequeue(){
return array.removeFirst();
}
@Override
public E getFront(){
return array.getFirst();
}
@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();
}
public static void main(String[] args){
ArrayQueue<Integer> queue = new ArrayQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2) {
queue.dequeue();
System.out.println(queue);
}
}
}
}
2、循环队列
数组队列出队操作的时间复杂度是 O(n) ,原因是因为每出队一个元素,队列就要整体向前移动一位。
为了减小这个复杂度,我们采用循环队列。所以循环队列的核心思想是:出队时不用将后面所有元素向前移动,而是保持整体不动,队尾满时自动填充到被删除的空位(可以看成一个圈)。
增加两个定义:
front 指示队头下标;tail 指示队尾下一个元素下标。
当 front == tail 时,队列为空
当(tail + 1)% c == front 时,队列为满(在这里有意识地浪费一个capacity)
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 boolean isEmpty(){
return front == tail;
}
@Override
public int getSize(){
return size;
}
@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("Cannont 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];
}
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 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<>();
// 将 0~7 入队
for(int i = 0 ; i < 8 ; i ++){
queue.enqueue(i);
System.out.println(queue);
// 每入队 3 个元素,出队 1 个元素
if(i % 3 == 2) {
queue.dequeue();
System.out.println(queue);
}
}
}
运行结果:
3、比较数组队列和循环队列的性能差异
import java.util.Random;
public class Main {
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) {
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");
}
}
运行结果:
可以看到,循环队列的性能远远优于数组队列。