1. 程式人生 > 其它 >Java資料結構(佇列),陣列模擬佇列實現

Java資料結構(佇列),陣列模擬佇列實現

技術標籤:JavaSEjava佇列資料結構演算法

佇列

佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(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;
    }
}