队列,它也是一种线性结构,它 和栈的不同之处,就是队列只能从一端(队尾)添加元素,只能从另一端取出元素。队列其实就是类似我们生活中的排队一样(但不允许插队)。
队列是一种先进先出(First In First Out)的数据结构,简称FIFO,允许插入的一端被称为队尾,,允许删除的一端称为队首。
数组队列的操作
package com.hqw.queue;
/**
* @author hqw521@qq.com
* @date 2018/6/5 11:02
*/
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e);
E dequeue();
E getFront();
}
数组队列的操作实现
package com.hqw.queue;
import com.hqw.array.Array;
/**
* @author hqw521@qq.com
* @date 2018/6/5 11:03
*/
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();
}
@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: front[");
for (int i = 0; i < getSize(); i++) {
res.append(array.get(i));
if (i != getSize() - 1) {
res.append(", ");
}
}
res.append("]tail");
return res.toString();
}
}
所谓的入队列操作,其实就是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为O(1),而与栈不同的是,队列元素的出列是在队头,即下标为0的位置,那也就意味着,对列中的所有元素都需要向前移动,以保证队列的队头,也就是下标为0的位置不为空,此时时间复杂度为O(n)。
但是想想,出队列时,不一定要全部移动,也就是说,队头不需要一定在下标为0的位置。如下图所示
在循环队列中,我们又引入了两个元素,front和tail,front指向队头元素,tail指向队尾元素。当front == tail时,队列为空。
当指针指向如下图所示时
就会出现“假溢出”现象,而解决假溢出的办法就是后面满了,就在从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。
刚才前面提到front == tail时,队列为空,但在这种循环队列中,队列满时,front 与tail也是相等的。为了解决这种问题,我们需要修改其条件,保留一个元素空间。也就是说,队列满时,数组中其实还有一个空闲的单元。如下图所示的两种情况:
在这时,我们就说队列已经满了。队列的最大尺寸为capacity,队列满的条件是(tail + 1) % capacity == front(取摸“%”的 目的是为了解决tail与front大小的问题)。通用的计算队列长度公式为:(tail - front + capacity)% capacity。在这里我就不解释这两个公式,有兴趣的朋友可以去了解一下。
实现循环队列的代码。
package com.hqw.queue;
/**
* @author hqw521@qq.com
* @date 2018/6/5 19:45
*/
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front;
private int tail;
public LoopQueue(int capactiy) {
data = (E[]) new Object[capactiy + 1];
front = 0;
tail = 0;
}
public LoopQueue() {
this(10);
}
/**
* 获取队列大小
* @return
*/
public int getCapacity() {
return data.length - 1;
}
/**
* 获取队列长度
* @return
*/
@Override
public int getSize() {
return (tail - front + data.length) % data.length;
}
/**
* 查看队列是否为空
* @return
*/
@Override
public boolean isEmpty() {
return front == tail;
}
/**
* 入队
* @param value
*/
@Override
public void enqueue(E value) {
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = value;
tail = (tail + 1) % data.length;
}
/**
* 出队列
* @return
*/
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("Queue is empty, dequeue failed");
}
E temp = data[front];
front = (front + 1) % data.length;
if (getSize() <= getCapacity() / 4 && getCapacity() / 2 != 0) {
resize(getCapacity() / 2);
}
return temp;
}
/**
* 获取队首元素
* @return
*/
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("Queue is empty");
}
return data[front];
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(String.format("Queue: size = %d, capactiy = %d\n", getSize(), getCapacity()));
stringBuilder.append("front [");
for (int i = front; i != tail; i = (i + 1) % data.length) {
stringBuilder.append(data[i]);
if ((i + 1) % data.length != tail) {
stringBuilder.append(", ");
}
}
stringBuilder.append("]tail");
return stringBuilder.toString();
}
private void resize(int newCapacity) {
E[] newData = (E[])new Object[newCapacity + 1];
for (int i = 0; i < getSize(); i++) {
newData[i] = data[(front + i) % data.length];
}
tail = getSize();
data = newData;
front = 0;
}
}