資料結構與演算法--佇列的陣列實現
阿新 • • 發佈:2020-09-13
佇列介紹
1.佇列是一個**有序列表**,可以使用**陣列和連結串列**實現
2.**先入先出**的原則;
佇列分類
順序佇列
順序佇列一般會設定兩個指標(front和rear)進行管理,front指向佇列的**第一個**元素,rear指向**最後一個元素的下一個位置**,**初始值都為0**。
每次在隊尾插入一個元素是:rear+1;
每次在隊頭刪除一個元素是:front+1;當front+rear時佇列為空。
順序佇列的溢位現象: “下溢”現象:當佇列為空時,做出隊運算產生的溢位現象。“下溢”是正常現象,常用作**程式控制轉移**的條件。 “真上溢”現象:當佇列滿時,做入佇列運算產生空間溢位現象。“真上溢”是一種出錯狀態,須要避免 “假上溢”現象:由於入隊和出隊操作中,頭指標只增加不減少,使被刪除元素的空間永遠無法重新利用。當佇列中實際元素個數小於向量空間的規模時,也可能由於尾指標已超越向量空間的上界而不能入隊。 順序佇列的缺陷: 當rear增加到指向分配的連續空間之外時,佇列無法再插入新元素,但這時往往還有大量可用空間未被佔用,這些空間是已經出隊的佇列元素曾經佔用過的儲存單元。也就是陣列使用一次之後不能再使用了,沒有達到複用的效果
缺陷解決思路:
(1)佇列元素的出列是在隊頭,即下標為0的地方,每次出隊佇列中的所有元素都要向前移動,保證佇列的隊頭,就是下標為0的位置不為空,此時時間複雜度為o(n),(每次出佇列時所有元素都要前移,效能有所降低)
(2) 可以設定front指標個rear指標越界時,從頭開始迴圈,即迴圈佇列
迴圈佇列
定義:無論插入或刪除,一旦rear指標+1或front+1時超出了所分配的佇列空間,就讓它指向這片連續空間的起始位置,之中頭尾相接的順序儲存結構成為迴圈佇列。 front指標指向佇列的**第一個**元素,rear指標只想**最後一個元素的下一個位置**,初始值都為0。當**front==rear**時為空佇列。如圖:
問題來了呀!!!下圖再入列a7時rear==front,此時就不是空佇列,與上面矛盾了啊
所以當佇列滿時,我們修改其條件,保留一個元素空間。也就是說佇列滿時,陣列中還有一個空閒單元,如下圖就是佇列滿啦:
陣列最大容量為QueueSize,佇列滿的條件(rear+1)% QueueSize == front(%的目的就是為了整合front和rear大小的問題)
佇列長度分為兩段,一段是QueueSize-front,另一段是0+rear,加在一起**佇列的長度為(rear-front+QueueSize)%QueueSize。
迴圈佇列程式碼如下:
`
import java.util.Scanner; public class CircleArrayQueueDemo { public static void main(String[] args) { System.out.println("測試陣列模擬環形佇列···"); //建立一個環形佇列 CircleArray queue = new CircleArray(4);//有效資料最大為3 char key = ' ';//接收使用者輸入 Scanner scanner = new Scanner(System.in); boolean loop = true; //輸出一個選單 while (loop) { System.out.println("s(show):顯示佇列"); System.out.println("e(exit):退出程式"); System.out.println("a(add):新增資料到佇列"); System.out.println("g(get):從佇列取出資料"); System.out.println("h(head):檢視佇列頭的數"); key = scanner.next().charAt(0);//接收一個字元 switch (key) { case 's': queue.showQueue(); break; case 'a': System.out.println("輸出一個數"); int value = scanner.nextInt(); queue.addQueue(value); break; case 'g': try { int res = queue.getQueue(); System.out.printf("取出的資料是%d\n", res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'h': try { int res = queue.headQueue(); System.out.printf("佇列頭的資料是%d\n", res); } catch (Exception e) { System.out.println(e.getMessage()); } break; case 'e': scanner.close(); loop = false; break; default: break; } } System.out.println("程式退出···"); }} class CircleArray { private int maxSize; //front優化後:front指向佇列的第一個元素,arr[front]是佇列的第一個值,front初始值為0 private int front; //rear優化後:rear指向佇列的最後一個元素的後一個位置,空出一個位置做為約定,rear初始值為0 private int rear; private int[] arr; public CircleArray(int arrMaxSize) { maxSize = arrMaxSize; arr = new int[maxSize]; //預設為0 //front = 0; //rear = 0; } public boolean isFull() { return (rear + 1) % maxSize == front; } public boolean isEmpty() { return rear == front; } public void addQueue(int n) { if (isFull()) { System.out.println("佇列滿,無法新增資料!"); return; } //直接將資料加入 arr[rear] = n; //將rear後移,必須考慮取模 rear = (rear + 1) % maxSize; } public int getQueue() { if (isEmpty()) { throw new RuntimeException("佇列空,無法取出資料!"); } //分析出front是指向佇列的第一個元素 //1.先把front對應的值保留到一個臨時變數中 //2.將front後移,考慮取模 //3.將臨時儲存的變數返回 int value = arr[front]; front = (front + 1) % maxSize; return value; } public void showQueue() { if (isEmpty()) { System.out.println("佇列空,沒喲資料!"); return; } for (int i = front; i < front + size(); i++) { System.out.printf("add[%d]=%d\n", i % maxSize, arr[i % maxSize]); } } //求出當前佇列有效的個數 public int size() { return (rear + maxSize - front) % maxSize; } public int headQueue() { if (isEmpty()) { throw new RuntimeException("佇列空,沒有資料!"); } return arr[front]; }}
`