簡介

佇列(Queue)中文也翻作隊列,顧名思義是一種像排隊一樣的概念,以生活中的情況為例如下圖

人們一個接一個的從隊伍後面加入排隊,而窗口的服務人員則從最前面的民眾一個接一個處理事務;當然現實生活是會有中途離開的人,而在程式世界裡面一般情況是不會有。
在這個模式下我們可以知道他是一種先進先出(First-In-First-Out, FIFO)的排程,而在此資料結構中至少會實作兩個操作:
1.enqueue:將資料放入佇列尾端。(註:C++中用push、Java用offer、也有add等不同的用字)
2.dequeue:取出佇列前端之資料。(註:C++中用pop、Java用poll、也有remove等不同的用字)
有時候也會多實作一些額外的操作以方便使用,例如:
1.peek:看佇列前端的資料而不取出。(註:也有front等不同的用字)
2.size:取得佇列的數目。
在實作上一般使用連結串列(LinkedList)來實作,使用陣列同樣可以達成,但較為複雜:
連結串列
用指標將資料串起來,將新的東西不斷接在最後面,而取出時則移除最前面的東西即可。由於加入和移除的端點不同,如果只記住第一個節點的話,在加入資料時,必須每次都向後找到最後一個節點會耗費許多時間,為了讓加入和移除都在O(1)的時間完成,可以同時記錄最前面和最後面的節點。
陣列
佇列建立時即建立一個陣列,但由於加入和刪除的端點不同,所以我們需要一個索引來記錄開始位址和一個索引來記錄結束位址,如下圖所示:

然而我們會發現,不斷的enqueue和dequeue之後,佇列的範圍會慢慢往後移,直到陣列的盡頭,為了永續經營,我們必須重新使用前面的部分,所以我們還必須多進行一個轉換的動作。
實作上我只有額外多紀錄開始索引,而利用序列數目、陣列長度和簡單的取餘數方式來算出結束索引,公式如下:
下一個佇列索引位址 = (開始索引位址 + 序列數目) % 陣列長度
以上圖情況為例:
(0 + 3) % 6 = 3
(3 + 3) % 6 = 0
(4 + 3) % 6 = 1
另外,在實作佇列擴充,進行陣列的複製動作時,需將陣列元素重新整理,將開始位址的元素複製到新陣列的起始位置,否則資料會亂掉,如下圖所示: