1、什么是队列

队列也是一种线性结构

相比数组,队列对应的操作是数组的子集

只能从一端(队尾)添加元素,只能从另一端(队首)取出元素。

队列是一种先进先出的数据结构( 先到先得)

First In First Out(FIFO)

 

2、自定义队列

1) 定义接口



public interface IQueue<E> {
int getSize(); // 时间复杂度 O(1) 均摊
boolean isEmpty(); // 时间复杂度 O(1)
void enqueue(E e); // 时间复杂度 O(1)
E dequeue(); // 时间复杂度 O(n)
E getFront(); // 时间复杂度 O(1)
}


  

2、自定义队列实现

基于自定义数组ArrayQueue



public class ArrayQueue<E> implements IQueue<E> {

private CustomArray<E> array;

public ArrayQueue(int capacity){
array = new CustomArray<E>(capacity);
}

public ArrayQueue(){
array = new CustomArray<E>();
}

public int getSize() {
return array.getSize() ;
}

public boolean isEmpty() {
return array.isEmpty();
}

public int getCapacity(){
return array.getCapacity();
}

public void enqueue(E e) {
array.addLast(e);
}

public E dequeue() {
return array.removeFirst();
}

public E getFront() {
return array.getFirst();
}

@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(String.format("Queue"));
sb.append("队首 [");
for(int i = 0; i< array.getSize(); i++){
sb.append(array.get(i));
if(i != array.getSize() -1){
sb.append(", ");
}
}
sb.append("] 队尾");
return sb.toString();
}

public static void main(String[] args) {
ArrayQueue<Integer> queue = new ArrayQueue<Integer>();
for(int i = 0; i < 10; i++){
queue.enqueue(i);
}
System.out.println(queue); // 输出: Queue队首 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); // 输出: 移除一个队首元素=> Queue队首 [1, 2, 3, 4, 5, 6, 7, 8, 9] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); //输出: 移除一个队首元素=> Queue队首 [2, 3, 4, 5, 6, 7, 8, 9] 队尾
}
}


  

3、循环队列

前面的自定义队列,出队列dequeue方法的时间复杂度为O(n), 这个复杂度产生的原因?

队列(动态数组实现自定义队列)_自定义

 

 

能不能在删除队首元素后,后面的元素不往前移动1位呢?这里就可以用到循环队列

队列(动态数组实现自定义队列)_自定义_02

 

循环队列实现



public class LoopQueue<E> implements IQueue<E> {

private E[] data;
//队首元素的索引
private int front;
//队尾元素的索引+1
private int tail;
private int size;
public LoopQueue(int 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;
}


public int getSize() {
return size;
}

public boolean isEmpty() {
return front == tail;
}

public void enqueue(E e) {
if((tail +1) % data.length == front){
resize(getCapacity() *2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
size++;

}

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;
}

private void resize(int newCapaCity) {
E[] newData = (E[])new Object[newCapaCity +1];
for(int i = 0; i < size; i++){
//i索引所在的数据,可能发生了偏移
newData[i] = data[(i+ front) % data.length];
}
data = newData;
front = 0;
tail = size;
}



public E getFront() {
if(isEmpty()){
throw new IllegalArgumentException("Queue is empty.");
}
return data[front];
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(String.format("Loop Queue size= %d, capacity = %d ", this.size, getCapacity()));
sb.append("队首 [");
for(int i = front; i != tail; i = (i +1) % data.length ){
sb.append(data[i]);
//不是最后一个元素
if( (i + 1) % data.length != tail){
sb.append(", ");
}
}
sb.append("] 队尾");
return sb.toString();
}

public static void main(String[] args) {
LoopQueue<Integer> queue = new LoopQueue<Integer>();
for(int i = 0; i < 5; i++){
queue.enqueue(i);
}
System.out.println(queue); // 输出: Loop Queue size= 5, capacity = 10 队首 [0, 1, 2, 3, 4] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); // 输出: 移除一个队首元素=> Loop Queue size= 4, capacity = 10 队首 [1, 2, 3, 4] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); //输出: 移除一个队首元素=> Loop Queue size= 3, capacity = 10 队首 [2, 3, 4] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); //输出: 移除一个队首元素=> Loop Queue size= 2, capacity = 5 队首 [3, 4] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); //输出: 移除一个队首元素=> Loop Queue size= 1, capacity = 2 队首 [4] 队尾

queue.dequeue();
System.out.println("移除一个队首元素=> " + queue); //输出: 移除一个队首元素=> Loop Queue size= 0, capacity = 1 队首 [] 队尾
}
}  


总结: 循环队列实现了dequeue为O(1) 均摊 的时间复杂度。

 

4、数组队列和循环队列的比较

编写如下测试代码:



public class Test {

//测试实验队列q运行opCount个enqueue和dequeue操作所需要的时间,单位: 秒
private static double testQueue(IQueue<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<Integer>();
double time1 = testQueue(arrayQueue, opCount);
System.out.println("数组队列ArrayQueue, time: " + time1 + "秒");

LoopQueue<Integer> loopQueue = new LoopQueue<Integer>();
double time2 = testQueue(loopQueue, opCount);
System.out.println("循环队列LoopQueue, time: " + time2 + "秒");

}


}  


输出结果: 



数组队列ArrayQueue, time: 4.540920463秒
循环队列LoopQueue, time: 0.054913962秒  


可以看到循环队列比数字队列时间相差近100倍。