用 JavaScript 學習資料結構和演算法:佇列(Queue)篇
什麼是佇列(Queue)?
佇列(Queue)是一種先進先出(First In First Out, FIFO)的有序串列(Ordered List),與堆疊(Stack)後進先出(Last In First Out, LIFO)不同的是佇列(Queue)的新增和刪除元素是發生在不同端,新增元素在尾部、移除元素在頂部,最新加入的元素會從尾部排入。在一般生活中比較常見的例子是電影院排隊買票、小七便利商店排隊付款(當然不能有人想插隊啦),在計算機科學領域佇列(Queue)應用也十分常見,像是印表機列印順序排程(當我們點選列印鍵時,第一個列印文件會先被列印,後續送來的文件會依序列印)或是一般作業系統的工作佇列(job queue)、I/O Buffer 緩衝區,也是透過先進先出來處理。
以下是佇列(Queue)幾個特性:
- 佇列(Queue)是一組相同性質的元素組合
- 具有先進先出(First In, First Out, FIFO)特性
- 新增元素時發生在 Rear 後端
- 刪除元素時發生在 Front 前端
- 新增/刪除(Enqueue/Dequeue 或 Add/Delete)元素是發生在不同端
用陣列(Array)實作佇列(Queue)
接下來我們將使用 JavaScript 的一維陣列來實作佇列(Queue),其基本步驟如下:
宣告佇列類別
1
2
3function Queue() {
// 這裡將放置佇列的屬性和方法
}宣告一個一維陣列(Array)
首先我們使用一個一維陣列(Array)當做儲存佇列元素的資料結構,這部份和我們之前提到的堆疊(Stack)類似,只是在佇列新增刪除元素時是在不同端點進行(這邊使用
let
讓scope
維持在function
中):1
2
3function Queue() {
let items = [];
}建立佇列可用方法(method)
- enqueue(element):於佇列尾端新增一個或多個元素
1
2
3this.enqueue = function(element) {
items.push(element);
};- dequeue():刪除佇列第一個(頭部)的元素,並回傳被移除的元素(在
JavaScript
中shift()
用於移除陣列第一個元素items[0]
,也就是頭部)
1
2
3this.dequeue = function() {
return items.shift();
}- front():回傳佇列中第一個元素,但佇列本身不作任何更動
1
2
3this.front = function() {
return items[0];
}- isEmpty():如果佇列中不包含任何元素,則回傳
true
,反之回傳false
1
2
3this.isEmpty = function() {
return items.length === 0;
}- size():回傳佇列所包含的元素個數,即為回傳陣列的
length
1
2
3this.size = function() {
return items.length;
}
完整佇列(Queue)類別:
1 | function Queue() { |
使用 Queue 類別
1 | // 由於上面我們已經建立好了 Queue 的類別,我們這邊直接使用。亦可以瀏覽器 console 直接看結果 |
優先級佇列(Priority Queue)
優先級佇列(Priority Queue)是一般佇列的修改版本,為一種不必遵守佇列特性-FIFO(先進先出)的有序串列。其規定每個元素都要有優先級,優先級最高的會最早獲得服務,若是優先級相同則看排列順序。優先佇列可以利用陣列結構及鏈結串列來實作。在生活中我們也可以看到優先級佇列(Priority Queue)的真實應用,例如:VIP 會員可以優先排隊進場、道路行駛時救護車優先於其他車輛,甚至是急救時也會有病情嚴重分類。於計算機科學領域中,CPU 工作排程也常會用到優先佇列。
建立優先級佇列(Priority Queue)類別
1 | function PriorityQueue() { |
使用優先級佇列(Priority Queue)類別
1 | // 由於上面我們已經建立好了 PriorityQueue 的類別,我們這邊直接使用。亦可以瀏覽器 console 直接看結果 |
環狀佇列(Circular Queue)
環狀佇列(Circular Queue)是指一種環形結構的佇列,它是利用一種 Q[0: N-1] 的一維陣列,同時 Q[0] 為 Q[N-1] 的下一個元素。由於一般佇列會遇到明明前端頭部尚有空間,但再加入元素時卻發現此佇列已滿。此時的解決方法就是使用環形佇列(Circular Queue)。
環狀佇列(Circular Queue)特性如下:
- 環狀佇列是一種環形結構的佇列
- 環狀佇列最多使用(N-1)個空間
- 指標 Front 永遠以逆時鐘方向指向佇列前端元素的前一個位置 Q[N]
- 指標 Rear 則指向佇列尾端的元素
若再加入一個項目時,Rear 等於 0,也就是 Front 等於 Rear,如此無法分辨此時佇列是空的還是滿的。因此,環形佇列必須浪費一個空格。當 Front 等於 Rear 時,環形佇列為空的。當(Rear+1)Mod N 等於 Front 時,環形佇列為已滿,通常環狀佇列最多使用(N-1)個空間。
總結
在這章我們學到了:
- 什麼是佇列(Queue)?它和堆疊(Stack)有什麼差別?
- 如何使用
JavaScript
建立Queue
類別? - 優先級佇列(priority queue)是什麼?有何特性?
- 環狀佇列(circular queue)是什麼?有何特性?
事實上,除了上述介紹的一般佇列、優先級佇列和環狀佇列外,還有雙向佇列(不像堆疊的 LIFO 或佇列的 FIFO,其允許在兩端都可以新增刪除元素)等特殊佇列。若是對於佇列(Queue)掌握度還不夠的讀者不妨用生活的情境進行聯想,想想你平常在電影院或是大賣場買東西排隊結帳的情景,會更容易幫助理解喔!
延伸閱讀
- [Algorithm][C / C++] 佇列(Queue)、環狀佇列(Circular Queue)
- 佇列(Queue)
- [ 資料結構 小學堂 ] 佇列 : 佇列的應用 (環狀佇列)
- 資料結構第4-7章補充教材
(image via stack-machine、wikipedia)
關於作者:
@kdchang 文藝型開發者,夢想是做出人們想用的產品和辦一所心目中理想的學校。A Starter & Maker. JavaScript, Python & Arduino/Android lover.:)
留言討論