Queue
隊列(queue)簡稱隊,它同堆棧一樣,也是一種運算受限
的線性表,其限制是僅允許 在表的一端進行插入,而在表的另一端進行刪除。在隊列中把插入數據元素的一端稱為隊尾(rear),刪除數據元素的一端稱為隊首(front)。向隊尾插入元素稱為進隊或入隊,新元素 入隊后成為新的隊尾元素;從隊列中刪除元素稱為離隊或出隊,元素出隊后,其后續元素成 為新的隊首元素。 由于隊列的插入和刪除操作分別在隊尾和隊首進行,每個元素必然按照進入的次序離隊,也就是說先進隊的元素必然先離隊,所以稱隊列為先進先出表(First In First Out,簡稱 FIFO)。
順序存儲結構
ArrayQueue
在隊列的順序存儲實現中,我們可以將隊列當作一般的表用數組加以實現,但這樣做的 效果并不好。盡管我們可以用一個指針 last 來指示隊尾,使得 enqueue 運算可在Ο(1)時間內 完成,但是在執行 dequeue 時,為了刪除隊首元素,必須將數組中其他所有元素都向前移動 一個位置。這樣,當隊列中有 n 個元素時,執行 dequeue 就需要Ο(n)時間。為了提高運算的效率,我們用另一種方法來表達數組中各單元的位置關系。設想數組 A[0.. capacity-1]中的單元不是排成一行,而是圍成一個圓環
public class ArrayQueue<T> extends AbstractList<T> {
public ArrayQueue(int capacity) {
this.capacity = capacity + 1;
this.queue = newArray(capacity + 1);
this.head = 0;
this.tail = 0;
}
public void resize(int newcapacity) {
int size = size();
if (newcapacity < size)
throw new IndexOutOfBoundsException("Resizing would lose data");
newcapacity++;
if (newcapacity == this.capacity)
return;
T[] newqueue = newArray(newcapacity);
for (int i = 0; i < size; i++)
newqueue[i] = get(i);
this.capacity = newcapacity;
this.queue = newqueue;
this.head = 0;
this.tail = size;
}
@SuppressWarnings("unchecked")
private T[] newArray(int size) {
return (T[]) new Object[size];
}
public boolean add(T o) {
queue[tail] = o;
int newtail = (tail + 1) % capacity;
if (newtail == head)
throw new IndexOutOfBoundsException("Queue full");
tail = newtail;
return true; // we did add something
}
public T remove(int i) {
if (i != 0)
throw new IllegalArgumentException("Can only remove head of queue");
if (head == tail)
throw new IndexOutOfBoundsException("Queue empty");
T removed = queue[head];
queue[head] = null;
head = (head + 1) % capacity;
return removed;
}
public T get(int i) {
int size = size();
if (i < 0 || i >= size) {
final String msg = "Index " + i + ", queue size " + size;
throw new IndexOutOfBoundsException(msg);
}
int index = (head + i) % capacity;
return queue[index];
}
public int size() {
// Can't use % here because it's not mod: -3 % 2 is -1, not +1.
int diff = tail - head;
if (diff < 0)
diff += capacity;
return diff;
}
private int capacity;
private T[] queue;
private int head;
private int tail;
}
鏈式存儲結構
隊列的鏈式存儲可以使用單鏈表來實現。為了操作實現方便,這里采用帶頭結點的單鏈 表結構。根據單鏈表的特點,選擇鏈表的頭部作為隊首,鏈表的尾部作為隊尾。除了鏈表頭 結點需要通過一個引用來指向之外,還需要一個對鏈表尾結點的引用,以方便隊列的入隊操 作的實現。為此一共設置兩個指針,一個隊首指針和一個隊尾指針,如圖所示,隊首針指(front)向隊首元素的前一個結點,即始終指向鏈表空的頭結點; 隊尾指針(rear)指向隊列當前隊尾元素所在的結點。當隊列為空時,隊首指針與隊尾指針均指向空的頭結點。在Java中沒有顯式的指針類型,然而實際上對象的訪問就是使用指針來實現的,即在Java中是使用對象的引用來替代指針的。
LinkedList
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
public boolean offer(E e) {
return add(e);
}
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pop() {
return removeFirst();
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
}