cover

排隊這件事,連資料結構都要遵守。

先講結論

Queue = 先進先出(FIFO)。從尾巴進、從頭出。所有操作 O(1)。它是 BFS、任務排程、訊息佇列的基礎。跟 Stack 的差異就一條:Stack 是「最後來的先走」,Queue 是「先來的先走」。

排隊的規則

enqueue →  [40] [30] [20] [10]  → dequeue
           rear            front
操作做什麼時間
enqueue(x)排到隊尾O(1)
dequeue()從隊頭離開O(1)
peek()看隊頭是誰O(1)

跟 Stack 一樣簡單,只是方向不同。

環形陣列:解決空間浪費

用普通陣列實作 Queue 有個問題——dequeue 之後前面會空出來:

dequeue 了 3 次:
[__, __, __, 40, 50]
              ↑ front

前面三格浪費掉了!

解法是讓指標繞回去,把陣列當成一個環:

實際陣列: [60, 70, __, 40, 50]
               ↑   ↑
             rear  front

邏輯上:   [40] [50] [60] [70]

關鍵就一行:index = (index + 1) % capacity。到底了就繞回 0。

這個技巧在 OS 的 kernel buffer、網路封包處理裡到處都是。Java 的 ArrayDeque 底層就是環形陣列。

Queue 無所不在

BFS(廣度優先搜尋) 用 Queue 一層一層往外擴展——這是 Queue 在演算法裡最重要的應用。沒有 Queue 就沒有 BFS。

訊息佇列(Message Queue) 像 RabbitMQ、Kafka,本質上就是分散式的 Queue。生產者丟訊息進去,消費者按順序拿出來處理。

印表機、鍵盤輸入、網路封包——所有「先來先處理」的場景都是 Queue。

你有沒有想過,為什麼 JavaScript 的事件處理叫「event queue」?因為瀏覽器把事件按照發生順序排進 Queue,event loop 從頭一個一個取出來處理。setTimeout(fn, 0) 不是「立即執行」,而是「排到隊尾,等前面的事做完」。

變體:Priority Queue

普通 Queue 是先來先走。但有時候你需要「最重要的先走」——這就是 Priority Queue。

它不是 FIFO,而是按照優先度出隊。底層通常用 Heap 實作,不是用陣列排序(那樣太慢了)。

急診室的叫號就是 Priority Queue:不是先來的先看,是最嚴重的先看。


Queue 的公平性讓人安心——但現實生活中,大部分的 Queue 最後都會變成 Priority Queue。