Java資料結構(佇列),陣列模擬佇列實現
佇列
佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作,佇列是一種操作受限制的線性表。進行插入操作(入口)的端稱為隊尾,進行刪除操作(出口)的端稱為隊頭。
佇列的特點:先進先出(FIFO,First In First Out),因此我們也稱佇列為FIFO表。
LinkedList類實現了Queue介面,在Queue介面中提供的方法可以實現佇列的相關操作。Queue介面擴充套件了Collection介面,常見的方法如下所示。
Queue介面中方法:
方法名 | 描述 |
boolean add(E e); boolean offer(E e); | 這兩個方法都是在尾部新增元素 add()會在長度不夠時丟擲異常; offer()則不會,只返回false |
E element(); E peek(); | 這兩個方法都是檢視頭部元素 ,返回頭部元素,但不改變佇列 element()會在沒元素時丟擲異常; peek()則返回null; |
E remove(); E poll(); | 這兩個方法都是刪除頭部元素,返回頭部元素,並且從佇列中刪除 remove()會在沒元素時丟擲異常; poll()返回null; |
boolean isEmpty(); | 判斷佇列是否為空,如果為空則返回true;否則返回false。 |
【示例】使用LinkedList實現佇列操作
public class Test { public static void main(String[] args) { // 佇列,先進先出 Queue queue = new LinkedList(); // 入隊,新增尾部元素 queue.offer(111); queue.offer(222); queue.offer(333); queue.offer(444); // 遍歷佇列中的所有元素,直到佇列為空為止 while(!queue.isEmpty()) { // 出隊,刪除頭部元素 System.out.println(queue.poll()); } } }
陣列模擬佇列實現:
以下采用陣列實現,初始化一個佇列陣列的空間長度為5,佇列有兩個標記,一個隊頭的位置front,一個隊尾的位置rear,初始都指向陣列下標為0的位置,如圖所示
在插入元素時,rear標記遞增+1,比如依次入隊11,22,33這三個元素,則當前佇列儲存情況如圖:
當前front為0,rear為3,接下來執行出隊操作,此處將11元素出隊,則front標記遞增+1,此時佇列的儲存情況如圖:
根據上面的圖例,我們可以通過rear-front獲得佇列中元素的個數。當front==rear時,此時佇列為空,而當rear==陣列長度時,此時將無法新增元素,我們可以設為佇列已滿,接下來我們就通過程式碼實現這個操作。
按照以上的操作,當front==rear時,那麼佇列是否已經滿呢?未必!因為front和rear在入隊和出隊操作中只增不減,因此front和rear都會最終指向佇列之外的儲存位置,此時雖然陣列為空,但也無法將元素入隊。
如何解決這個問題,我們引入了迴圈佇列。當rear的取值為陣列空間長度,此時如果陣列還有空閒的位置,將rear從新指向陣列的0索引處即可,如圖所示:
如果繼續入隊66和77這兩個元素,則佇列的儲存結構如圖:
在採用迴圈佇列實現的過程中,當佇列滿隊時,front等於rear,而當佇列空時,front也等於rear,為了區分兩種狀態,一般規定迴圈佇列的長度為”陣列長度-1”,即有一個位置不放元素,此時front==rear時為空隊,而當front==(rear+1)%陣列長度,說明對滿。
【示例】迴圈列列的實現
public class ArrayQueue<T> {
/**
* 模擬佇列的陣列
*/
private Object[] elementData;
/**
* 儲存隊首的指標
*/
private int front;
/**
* 儲存隊尾的指標
*/
private int rear;
/**
* 無參構造方法
*/
public ArrayQueue() {
// 設定陣列的預設空間長度為10
this(10);
}
/**
* 有參構造方法
* @param capcaity 設定陣列的空間長度
*/
public ArrayQueue(int capcaity) {
// 處理capcaity引數不合法的情況
if (capcaity < 0) {
throw new IllegalArgumentException("引數不合法異常,capcaity:" + capcaity);
}
// 建立指定空間長度的陣列
this.elementData = new Object[capcaity];
}
/**
* 獲取佇列中元素的個數
* @return 返回佇列中元素的個數
*/
public int getSize() {
if (rear > front) {
return rear - front;
}
else {
return (rear + elementData.length) - front;
}
}
/**
* 入棧操作
* @param element
*/
public void add(T element) {
// 判斷佇列是否已滿
if (isFull()) {
throw new RuntimeException("佇列已滿,無法執行入佇列操作");
}
// 執行入隊操作
elementData[rear] = element;
// 更新rear的值
rear = (rear + 1) % elementData.length;
}
/**
* 刪除佇列的首元素
* @return 返回被刪除的元素值
*/
public T remove() {
// 判斷佇列是否為空
if (isEmpty()) {
throw new RuntimeException("佇列為空,無法執刪除佇列操作");
}
// 獲取佇列首元素
T element = (T)elementData[front];
// 把front索引位置元素設定為預設值
elementData[front] = null;
// 更新front索引值
front = (front + 1) % elementData.length;
// 返回被刪除的隊首元素
return element;
}
/**
* 獲得佇列的首元素
* @return 返回佇列的首元素
*/
public T element() {
// 判斷佇列是否為空
if (isEmpty()) {
throw new RuntimeException("佇列為空,無法執獲取佇列操作");
}
// 獲取並返回佇列的首元素
return (T)elementData[front];
}
/**
* 判斷佇列是否已滿
* @return 佇列已滿,則返回true;佇列未滿,則返回false。
*/
private boolean isFull() {
return front == (rear + 1) % elementData.length;
}
/**
* 判斷佇列是否為空
* @return 佇列為空,則返回true;佇列不為空,則返回false。
*/
public boolean isEmpty() {
return front == rear;
}
}