
排隊這件事,連資料結構都要遵守。
先講結論
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。