Java陣列--自定義迴圈佇列(三)
阿新 • • 發佈:2018-12-19
自定義迴圈佇列
思考:
在上篇文章中在討論佇列的出隊操作時候,其時間複雜度分析為O(n),這是因為每次出隊操作會刪除佇列的對頭,從而導致其陣列後續的元素都會往前移動n-1次,那怎麼樣才能實現隊列出隊操作的時候其時間複雜度為O(1)呢?
迴圈佇列定義
- 佇列(queue)是隻允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
- 允許插入的一端稱為隊尾(Tail),允許刪除的一端稱為隊頭(Front),不允許在中間部位進行操作。
- 佇列具有先進先出(FIFO),後進後出(LILO)的特性。
- 其front和tail的索引座標能夠針對佇列容量進行迴圈變更,並且永遠有一個索引不儲存元素。
示意圖:
程式碼:
Queue泛型介面類:
/**
* 定義一個泛型的佇列介面
* @param <E>
*/
public interface Queue<E> {
//入佇列,將一個元素新增到對尾
void enqueue(E e);
//出對列,將一個元素從對頭刪除,並且返回這個元素
E dequeue();
//獲取佇列對頭的元素
E getFront();
//判斷這個棧是否為空
boolean isEmpty();
//獲取棧中的元素個數
int getSize();
}
LoopQueue泛型實現類:
/** * 基於陣列實現的迴圈佇列 * * @param <E> */ public class LoopQueue<E> implements Queue<E> { //用來儲存佇列元素的陣列 private E[] data; //佇列對頭的索引座標 private int front; //佇列對尾的索引座標 private int tail; //佇列中元素個數 private int size; /** * 無參構造方法 */ public LoopQueue() { this(10); } /** * 有參構造方法 * * @param capacity 佇列的容量 */ public LoopQueue(int capacity) { data = (E[]) new Object[capacity]; } /** * 獲取佇列的容量 * * @return 返回佇列的容量 */ public int getCapacity() { return data.length - 1; } /** * 判斷佇列元素是否為空 * * @return 返回true代表為空,false代表不為空 */ public boolean isEmpty() { return front == tail; } /** * 獲取佇列中元素的個數 * * @return 返回佇列中元素的個數 */ public int getSize() { return size; } /** * 向佇列中新增元素 * * @param e 待新增的元素 */ public void enqueue(E e) { if ((tail + 1) % data.length == front) { int newCapacity = data.length + (data.length >> 1); resize(newCapacity); } data[tail] = e; tail = (tail + 1) % data.length; size++; } /** * 刪除佇列隊頭元素 * * @return 返回佇列隊頭元素 */ @Override public E dequeue() { if (isEmpty()) throw new IllegalArgumentException("Cannot dequeue from an empty queue."); E ret = data[front]; data[front] = null; front = (front + 1) % data.length; size--; return ret; } /** * 獲取對列對頭的元素 * * @return 返回佇列隊頭元素 */ @Override public E getFront() { if (isEmpty()) throw new IllegalArgumentException("Cannot dequeue from an empty queue."); return data[front]; } /** * 迴圈佇列私有的擴容操作 每次擴容1.5倍 * * @param newCapacity 將要擴容的容量 */ 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; newData = null; } @Override public String toString() { StringBuffer res = new StringBuffer(); res.append("Queue:front ["); for (int i = front; i != tail; i = (i + 1) % data.length) { res.append(data[i]); if ((i + 1) % data.length != tail) res.append(", "); } res.append("] tail"); res.append(String.format(" , size = %d , capacity = %d", size, data.length)); return res.toString(); } }
陣列佇列的時間複雜度分析:
- void enqueue(E e); ----O(1) 均攤,這裡的均攤是指可能會觸發陣列的擴容操作。
- E dequeue();----O(1)。
- E getFront();----O(1) 。
- boolean isEmpty();----O(1) 。
- int getSize();----O(1) 。