佇列
基本介紹
佇列是一個先進先出(FIFO, First-In-First-Out)的線性表, 佇列只允許在後端(rear)進行插入操作, 在前端(front)進行刪除操作。
特點
- 先進先出
佇列操作如下圖所示
設計一個Queue
- 入隊
- 出隊
- 隊首
- 佇列長度
- 佇列是否為空
public interface Queue<E> {
// 入隊
void enqueue(E e);
// 出隊
E dequeue();
// 獲取隊首
E getFront();
int getSize();
boolean isEmpty();
}
複製程式碼
public class ArrayQueue<E> implements Queue<E> {
private Array<E> array;
public ArrayQueue(int capacity) {
this.array = new Array<>(capacity);
}
public ArrayQueue() {
this.array = new Array<>();
}
@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();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("Queue: front [");
for (int i = 0; i < array.getSize(); i ++) {
buff.append(array.get(i));
if (i < array.getSize() - 1)
buff.append(",");
}
buff.append("] tail");
return buff.toString();
}
}
複製程式碼
上面的程式碼底層是利用陣列來實現的佇列, 當我們進行出隊的時候(即隊首元素), 後面的所有元素都要向前移動一個單位。之後size--, 這是我們陣列底層刪除一個元素的操作。 由於所有元素都要向前移動一位這個操作在, 所以我們出隊操作是O(n)。
假如現在我們不進行元素位置的移動, 記錄一下當前的隊首在哪。記錄隊尾的位置新元素新增的位置。
通過下圖來看看迴圈佇列
迴圈佇列的設計
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity) {
// 在計算迴圈陣列的時候, 我們需要浪費一個空間, 這樣在計算tail + 1 == front的時候就可以用到或者說(tail + 1) % capacity == front
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue() {
this(10);
}
@Override
public void enqueue(E e) {
// 這裡的取餘計算使用的是data.length, 但是resize方法使用的是getCapacity()方法
// 區別就是getCapacity裡面長度-1, 畢竟我們多開了一個陣列空間
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2) ;
}
data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity + 1];
for (int i = 0 ; i < size; i ++) {
newData[i] = data[(i + front) % data.length];
}
data = newData;
front = 0;
tail = size;
}
@Override
public E dequeue() {
if (isEmpty()) {
throw new IllegalArgumentException("佇列為空");
}
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;
}
@Override
public E getFront() {
if (isEmpty()) {
throw new IllegalArgumentException("佇列為空");
}
return data[front];
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return tail == front;
}
public int getCapacity() {
return data.length - 1;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(String.format("Queue size = %d , capacity = %d\n", size, getCapacity()));
buffer.append("front [");
for (int i = front ; i != tail; i = (i + 1) % data.length) {
buffer.append(data[i]);
if ((i + 1) % data.length != tail) {
buffer.append(", ");
}
}
buffer.append("] tail");
return buffer.toString();
}
}
複製程式碼