队列可以基于数组或者链表实现。如果选择基于数组实现,效率会更高,但是数组长度固定,需要在队列长度超限或者队列长度过短时对数组长度进行调整。
如果直接采用数组存储队列,当队列头部出列一个元素后要依次迁移大量后继元素增加无谓开销。改进的方式是采用循环数组,循环数组用两个“指针”标示队列头尾部,当队列头部元素出列,则向后移动头指针,当队列尾部入列元素,则向后移动尾指针。
注意,一旦尾指针被移动到数组最末尾,则应当将尾指针归0,再入列新元素(当然此时头指针肯定是指向了数组的中间某个位置,头尾指针不会交叉,因为当队列长度达到数组长度时已经将数组扩容了)。
代码如下:
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 利用循环数组实现的队列数据结构,能够通过修改队列head来减少dequeue时的数据迁移量
* @param <Item> 队列元素数据类型
*/
public class ModifiedResizingArrayQueue<Item> implements Iterable<Item> {
private Item[] queue; //用于存储队列的数组
private int size; //队列的长度
private int head; //队列的头部
private int tail; //队列的尾部
public ModifiedResizingArrayQueue() {
this.queue = (Item[]) new Object[2];
this.size = 0;
this.head = 0;
this.tail = 0;
}
public boolean isEmpty() {
return size == 0;
}
public int getSize() {
return size;
}
private void resize(int newSize) {
assert newSize >= size;
Item[] newQueue = (Item[]) new Object[newSize];
for (int i = 0; i < size; ++i) {
newQueue[i] = queue[(head + i) % queue.length];
}
queue = newQueue;
head = 0;
tail = size;
}
public void enqueue(Item item) {
//当队列占满数组时,将数组扩容一倍
if (size == queue.length) {
resize(2 * queue.length);
}
queue[tail] = item;
tail++;
//如果队列尾部已经到达数组尾部,则先将队列尾部指向数组最前才能添加元素
if (tail == queue.length) {
tail = 0;
}
size++;
}
public Item dequeue() {
//当队列短于数组1/4时,将数组缩减一半
if (size == queue.length / 4) {
resize(queue.length / 2);
}
if (size == 0) {
throw new RuntimeException("Queue empty!");
}
Item item = queue[head];
queue[head] = null;
head++;
size--;
return item;
}
@Override
public Iterator<Item> iterator() {
return new QueueIterator();
}
private class QueueIterator implements Iterator<Item> {
private int index = 0;
@Override
public boolean hasNext() {
return index < size;
}
@Override
public Item next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Item item = queue[(head + index) % queue.length];
index++;
return item;
}
}
public static void main(String[] args) {
ModifiedResizingArrayQueue<String> strQueue = new ModifiedResizingArrayQueue<>();
System.out.println("Empty? " + strQueue.isEmpty());
System.out.println("Current size: " + strQueue.getSize());
strQueue.enqueue("to");
strQueue.enqueue("be");
strQueue.enqueue("or");
System.out.println("Current size: " + strQueue.getSize());
System.out.println(strQueue.dequeue());
System.out.println(strQueue.dequeue());
System.out.println(strQueue.dequeue());
System.out.println("Current size: " + strQueue.getSize());
System.out.println("当队列为空继续出列则报错:");
strQueue.dequeue();
System.out.println("Current size: " + strQueue.getSize());
}
}