队列
队列的特点
- 队列也是一种线性结构
- 相比数组, 队列对应的操作是数组的子集
- 只能从一端(队尾)添加元素, 只能从另一端(队首)取出元素
- 队列是一种先进先出的数据结构(先到先得)
- First In First Out (FIFO)
队列接口的实现
Queue
- void enqueue(E)
- E dequeue()
- E getFront()
- int getSize()
- boolean isEmpty()
队列的实现
数组队列
使用数组实现一个队列
实现代码
package pers.jssd.queue;
/**
* 简单的用数组实现队列
*
* @author jssd
* Create 2019-07-24 8:56
*/
public class ArrayQueue<E> implements Queue<E>{
private Array<E> array;
public ArrayQueue() {
array = new Array<>();
}
public ArrayQueue(int capacity) {
array = new Array<>(capacity);
}
@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 int getSize() {
return array.getSize();
}
/**
* 取得队列的容量
* @return 队列的容量
*/
public int getCapacity() {
return array.getCapacity();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public String toString() {
return "ArrayQueue{" +
"array=" + array +
'}';
}
}
测试代码
package pers.jssd.test;
import pers.jssd.queue.ArrayQueue;
import pers.jssd.queue.Queue;
/**
* @author jssd
* Create 2019-07-24 8:53
*/
public class Main {
public static void main(String[] args) {
Queue<Integer> queue = new ArrayQueue<>();
for (int i = 0; i < 10; i++) {
queue.enqueue(i);
System.out.println("queue = " + queue);
if (i % 3 == 2) {
queue.dequeue();
System.out.println("queue = " + queue);
}
}
}
}
复杂度分析
ArrayQueue
- void enqueue(E) O(1) 均摊复杂度
- E dequeue() O(n)
- E getFront() O(1)
- int getSize() O(1)
- boolean isEmpty() O(1)
可以看到, 我们出队的时候, 每次删除数组的第一个元素, 所有元素都要向前移动一个位置, 所以复杂度是O(n)级别的. 那么, 我们能不能对此优化呢, 下面我们实现循环队列
循环队列
程序维护两个指针, 一个指向队首, 一个指向队尾. 入队的时候, 队尾加一, 出队的时候, 队首加一. 当指针加到数组的尾部的时候, 循环到数组的开头. 实现了一个循环的队列
实现代码
package pers.jssd.queue;
import java.util.Arrays;
/**
* 使用数组实现的循环队列, 当front==tail的时候, 队列为空, 当front == (tail+1) % capacity的时候, 队列已满
*
* @author jssd
* Create 2019-07-24 9:05
*/
public class LoopQueue<E> implements Queue<E> {
/**
* 底层用数组实现
*/
private E[] data;
/**
* 指向队首的指针
*/
private int front;
/**
* 指向队尾的指针
*/
private int tail;
@SuppressWarnings("unchecked")
public LoopQueue(int capacity) {
front = tail = 0;
data = (E[]) new Object[capacity + 1];
}
public LoopQueue() {
this(10);
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front) {
resize(data.length * 2);
}
data[tail] = e;
tail = (tail + 1) % data.length;
}
/**
* 扩容数组
*
* @param newCapacity 扩容新数组的容量
*/
@SuppressWarnings("unchecked")
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < getSize(); i++) {
newData[i] = data[(i + front) % data.length];
}
tail = getSize();
front = 0;
data = newData;
}
@Override
public E dequeue() {
if (front == tail) {
throw new IllegalArgumentException("error, can't dequeue from empty queue");
}
E ret = data[front];
front = (front + 1) % data.length;
if (getSize() < data.length / 4) {
resize(data.length / 2);
}
return ret;
}
@Override
public E getFront() {
return data[front];
}
@Override
public int getSize() {
return (tail + data.length - front) % data.length;
}
/**
* 取得队列容量
* @return 队列的容量
*/
public int getCapacity() {
return data.length - 1;
}
@Override
public boolean isEmpty() {
return front == tail;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Queue{").append("data=[");
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("], front=").append(front).append(", tail=").append(tail).append(", size=")
.append(getSize()).append(", capacity = ").append(getCapacity()).append("}");
return sb.toString();
}
}
复杂度分析
此时, dequeue()方法也变成了O(1)级别的复杂度(均摊), 所以整个循环队列是O(1)级别的复杂度
两种队列比较
代码比较两种队列在执行1000000的数据时, 用时差异
package pers.jssd.test;
import pers.jssd.queue.ArrayQueue;
import pers.jssd.queue.LoopQueue;
import pers.jssd.queue.Queue;
/**
* @author jssd
* Create 2019-07-24 9:49
*/
public class CompareQueue {
public static void main(String[] args) {
int opCount = 1000000;
ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
LoopQueue<Integer> loopQueue = new LoopQueue<>();
System.out.println("arrayQueue use time = " + test(arrayQueue, opCount));
System.out.println("loopQueue use time = " + test(loopQueue, opCount));
}
/**
* 测试队列执行一定次数使用的时间
* @param queue 执行的队列
* @param opCount 执行的次数
* @return 返回执行的时间
*/
@SuppressWarnings("unchecked")
private static double test(Queue queue, int opCount) {
long start = System.nanoTime();
for (int i = 0; i < opCount; i++) {
queue.enqueue(i);
if (i % 3 == 2) {
queue.dequeue();
}
}
long end = System.nanoTime();
return (end - start) / 1000000000.0;
}
}
时间差异如下
arrayQueue use time = 26.6430455
loopQueue use time = 0.0463479