1. 程式人生 > >java佇列--先進先出(迴圈佇列、鏈佇列)

java佇列--先進先出(迴圈佇列、鏈佇列)

佇列: 只允許在一端進行插入操作(隊尾),在另一端進行刪除操作(隊頭)。 佇列的特徵就是: 先進先出

佇列的思想及實現也同樣非常簡單。在生活中的各種常常都需要排隊進行,鍵盤中快取區作業系統中的作業排程等都有用到佇列先進先出的思想。在這裡同樣用一個示意圖展示佇列的基本思想。

下面筆者才用陣列儲存元素,實現了一個迴圈佇列。

佇列中最重要的一個方法就是佇列是否滿的判斷,

判斷佇列是否為滿。隊滿: rear + 2 = front 或 front + maxSize -2 = rear

判斷佇列是否為空。隊空: rear + 1 = front 或 front + maxSize -1 = rear

	/** 判斷佇列是否為空。隊空: rear + 1 = front 或 front + maxSize -1 = rear
	 *  通過陣列容量比佇列資料項的最大值大一,來區分對空和對滿。
	 */	
	public boolean isEmpty(){
		return (rear + 1 == front || front + maxSize -1 == rear);  
	}
	
	 /**判斷佇列是否為滿。 隊滿: rear + 2 = front 或 front + maxSize -2 = rear
	  * 通過陣列容量比佇列資料項的最大值大一,來區分對空和對滿。
	  */
	public boolean isFull(){
		return (rear + 2 == front || front + maxSize -2 == rear);
	}
	
獲取佇列大小(元素個數):可以通過隊頭隊尾計算出佇列的大小,也可以通過一個計數器,當入隊是加1,出隊是減1.
	/** 獲取佇列的大小
	 */
	public int queueSize(){
		if(rear >= front){
			return rear - front +1;
		}else {
			return maxSize - front + (rear + 1);
		}
	}
出隊入隊檢視隊頭元素:

入隊 入隊                          出隊出隊 

全部程式碼及測試: 

package org.TT.Queue;
/**
 * 迴圈佇列: 先進先出
 *        佇列同樣是個概念上的輔助工具。這裡採用陣列完成佇列的操作,仍然採用泛型
 *        佇列的資料項的入隊、出隊時間複雜度為常數 O(1)。
 *        通過隊頭隊尾指標的移動儲存所有資料位置不動,而不是移動資料項。
 *        在這裡,陣列的大小比佇列存放資料元素大1,主要是為了方便隊滿的判斷。
 */
public class Queue<T> {

	private int maxSize;   // 佇列最多容納數量
	private Object[] queueArray;
	private int front;   // 隊頭
	private int rear;	// 隊尾
	private int size;
	
	public Queue(int length) {
		maxSize = length;
		queueArray = new Object[maxSize];
		front = 0;   
		rear = -1; 
		size = 0;
	}
	
	/**	入隊: 先將rear(隊為指標) 加1, 後將資料項存入rear的位置。
	 *	當rear 指向maxSize -1 的位置時,將rear 設定為-1(迴圈佇列),加1 後存入資料項。
	 */
	public void enQueue(T str){
		
		if(isFull()){    // 入隊之前先檢查佇列是否已滿,已滿則丟擲異常。
				throw new RuntimeException("佇列已滿," + str + " 不能入隊!");
		}
		
		if(rear == maxSize -1){
			rear = -1;
		}
		queueArray[++rear] = str;  // 先將 rear 加1,後取值
		size++;
	}
	
	
	/**出隊: 先取出front 的值,然後將front 減1 
	 * 如果 front 超過了陣列的頂端,將 front 設定為 0(迴圈佇列)
	 */
	@SuppressWarnings("unchecked")
	public T deQueue(){
		
		if(isEmpty()){   // 出隊之前先檢查佇列是否為空, 為空則丟擲異常。
				throw new RuntimeException("佇列為空,不能出隊!");
		}
		
		T str = (T) queueArray[front++];  // 先去 queueArray[front] 的值,後將front 加1
		if(front == maxSize){
			front = 0;
		}
		size--;
		return str;
	}
	
	/**檢視對頭資料項
	 */
	@SuppressWarnings("unchecked")
	public T peek(){
		if(isEmpty()){   // 檢視隊頭時,判斷是否為空, 為空則丟擲異常。
				throw new RuntimeException("佇列為空!");
		}
		return (T) queueArray[front];
	}

	/** 判斷佇列是否為空。隊空: rear + 1 = front 或 front + maxSize -1 = rear
	 *  通過陣列容量比佇列資料項的最大值大一,來區分對空和對滿。
	 */	
	public boolean isEmpty(){
		return (rear + 1 == front || front + maxSize -1 == rear);  
	}
	
	 /**判斷佇列是否為滿。 隊滿: rear + 2 = front 或 front + maxSize -2 = rear
	  * 通過陣列容量比佇列資料項的最大值大一,來區分對空和對滿。
	  */
	public boolean isFull(){
		return (rear + 2 == front || front + maxSize -2 == rear);
	}
	
	/** 獲取佇列的大小
	 */
	public int queueSize(){
	/* 可以通過隊頭隊尾計算出佇列的大小,也可以通過一個計數器,當入隊是加1,出隊是減1.
	 if(rear >= front){
			return rear - front +1;
		}else {
			return maxSize - front + (rear + 1);
		}
	*/
		return size;
	}
	
	public static void main(String[] args) {
		Queue<String> queue = new Queue<>(5);
		queue.enQueue("a");
		queue.enQueue("b");
		queue.enQueue("c");
		queue.enQueue("d");
		queue.deQueue();
		queue.deQueue();
		
		System.out.println("佇列是否為空: " + queue.isEmpty() + "  佇列是否滿: " + queue.isFull());
		System.out.println("佇列大小:" + queue.queueSize());
		
		int size = queue.queueSize();
		for(int i = 0; i < size; i++){
			String str = queue.deQueue();
			System.out.print(str + " ");
		}
		
	}
	
}
  結果


 關於鏈佇列只是在單鏈表的基礎上多了簡單的修改,在單鏈表中新增一個尾指標即可,每次入隊、出隊只分別對單鏈表的隊尾和隊首進行插入和刪除在操作,由於鏈隊沒有隊滿的限制,所以相對於非常簡單,而判空操作只需判斷 front == rear 是否成立。 在這裡筆者就不列出鏈佇列。在java庫中也有佇列相關的介面及實現並且還有雙端佇列、優先佇列等類。